类定义
C++11中,基本类型成员变量可以在类中声明时给默认值。
- 声明定义都在类中
成员函数如果在类中定义,编译器可能会默认将其当成内联函数处理。
cpp
class Person
{
public:
void showInfo() {
cout << _name << "-" << _age << endl;
}
private:
char* _name;
int _age;
};
- 类声明在
.h文件中,成员函数定义在.cpp文件中.
person.h:
cpp
class Person
{
public:
void showInfo();
private:
char* _name;
int _age;
};
person.cpp:
cpp
#include "person.h"
//在类外定义成员时,需要使用::指明成员属于哪个类域。
void Person::showInfo(){
cout << _name << "-" << _age << endl;
}
类class与结构体struct的区别
C++需要兼容C语言,所以C++中struct可以定义结构体,也可以用来定义类。区别在于struct定义的类其成员默认访问权限是public,class定义的类成员默认访问权限是private。
this指针
this为一个const指针。
C++编译器给每个非静态的成员函数增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
cpp
class A1 {
public:
void f1() {
cout << _a << endl;
}
private:
int _a;
};
<---------经过编译器处理后,相当于------->
class A1 {
public:
void f1(A1 *const this) {
cout << this->_a << endl;
}
private:
int _a;
};
this指针是非静态成员函数的第一个形参,当对象调用成员函数时,将对象的地址作为实参传入。在const成员函数中,this指向的是const类型的对象。
this指针存在哪?
this指针作为函数参数,在调用前将值给到ecx寄存器,然后push ecx进栈中。
运算符重载
函数原型:返回值类型 operator操作符(参数列表)
- 基本类型的运算符不能重载。
.*::``sizeof?:.不能重载。
cpp
class Person
{
public:
// 左操作数为this。
bool operator==(const Person& p);
private:
int _age;
};
bool Person::operator==(const Person& p) {
return _age == p._age;
}
类的默认成员函数
构造函数
- 构造函数是一个特殊的成员函数,名字与类名相同,无返回值,创建类型对象时由编译器自动调用。
cpp
#include <iostream>
using namespace std;
class A1 {
public:
A1();
A1(int a);
private:
int _a;
};
//无参
A1::A1(){
_a = 0;
}
//带参
A1::A1(int a) {
_a = a;
}
int main()
{
A1 a1; //使用无参构造函数初始化
A1 aa1(10);//使用带参构造函数初始化
return 0;
}
- 构造函数的任务并不是开空间创建对象,而是初始化对象。
- 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数(并不准确,请见C++对象模型),一旦用户显式定义编译器将不再生成。
- C++编译器生成的默认构造函数,初始化基本类型仍然是随机值,会调用自定义类型的默认构造函数进行初始化。
- 无参构造函数,全缺省构造函数,编译器生成的默认构造函数都认为是默认构造函数,三者会冲突。
初始化列表
以下三种情况必须使用初始化列表进行初始化。
const成员- 引用成员
- 没有默认构造函数的自定义类型成员
原因:在构造函数中用户编写的语句调用前,所有成员变量已定义完成。const和引用类型在定义时就必须初始化完成,不能在构造函数中二次赋值;自定义成员则在定义时需要调用其默认构造函数,没有则只能使用初始化列表调用其别的构造函数来定义。
cpp
class C {
int val;
public:
C(int x) : val(x) {}
};
class A {
public:
A(int x,int& ref,int val):_x(x),_ref(ref),_c(val){}
private:
const int _x;
int& _ref;
C _c;
};
注意:初始化顺序为成员在类中声明的顺序,与初始化列表顺序无关。
以下代码为例:
cpp
class A
{
public:
A(int a)
:_a1(a)
, _a2(_a1)
{
}
void Print() {
cout << _a1 << " " << _a2 << endl;
}
private:
int _a2;
int _a1;
};
int main() {
A aa(1);
aa.Print();
}
// 输出:1 随机值
析构函数
- 无参数无返回值,不可重载。
- 对象生命周期结束时,系统自动调用析构函数。
- 若未显式定义,自动生成默认的析构函数(并不准确,请见C++对象模型)。编译器生成的默认析构函数会调用自定义类型成员的析构函数。
- 若类中有动态申请的资源,必须要写析构函数!避免造成资源泄露。
- 局部对象的释放,先定义的先入栈也就后释放。
以下代码输出:

cpp
class Person
{
public:
Person(const char* name);
~Person();
private:
char* _name;
};
Person::Person(const char* name) {
_name = new char[strlen(name) + 1];
strcpy(_name, name);
}
Person::~Person() {
cout << "free " << _name << endl;
delete[] _name;
}
int main()
{
Person p1("p1");
Person p2("p2");
return 0;
}
拷贝构造函数
- 其是构造函数的一个重载形式,只有一个形参,即本类类型对象的引用(一般使用
const修饰)。 - 用已存在的类类型对象创建新对象时(如函数传参,拷贝临时变量)由编译器自动调用。
- 类中涉及到资源申请时,必须写拷贝构造函数!否则系统默认生成的为浅拷贝。
cpp
class Person
{
public:
Person(const char* name);
~Person();
Person(const Person& p);
private:
char* _name;
};
Person::Person(const char* name) {
_name = new char[strlen(name) + 1];
strcpy(_name, name);
}
Person::Person(const Person& p) {
_name = new char[strlen(p._name) + 1];
strcpy(_name, p._name);
}
Person::~Person() {
cout << "free " << _name << endl;
delete[] _name;
}
C++11引入构造函数委托,可通过调用本类中的另一个构造函数来初始化当前对象。
cpp
// 构造函数委托给 Person(const char*)
Person::Person(const Person& p):Person(p._name) {}
赋值运算符重载
- 参数
const T&,返回值T&,检测是否是同一地址,返回*this。 - 赋值运算符不能重载成全局函数,只能是成员函数。
- 没有显示实现,则编译器自动生成,进行浅拷贝。
cpp
class Person
{
public:
Person& operator=(const Person& p);
private:
char* _name;
};
Person& Person::operator=(const Person& p) {
if (this != &p) {
// 1. 释放旧内存
delete[] _name;
// 2. 分配新内存
_name = new char[strlen(p._name) + 1];
// 3. 拷贝数据
strcpy(_name, p._name);
}
return *this;
}
cpp
int main(){
Person p1=p2; //调用拷贝构造函数
p1=p3; //调用赋值重载
}
静态(static)成员
- 静态成员为类对象所共享,不为某个对象独有,存在全局静态区。
- 静态成员变量必须在类外定义,定义时不需要添加
static关键字,其受访问控制符的影响。 - 静态成员可直接使用
类::静态成员的方式访问。 - 静态函数没有this指针,无法访问任何非静态成员。
友元
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
cpp
class Person
{
// 在该类中对该函数进行友元声明,则在该函数定义中可以直接访问p的私有变量
friend ostream& operator<< (ostream& _cout, const Person& p);
public:
explicit Person(int age);
private:
int _age;
};
Person::Person(int age):_age(age){}
ostream& operator<<(ostream& _cout, const Person& p)
{
_cout << "Person.age:" << p._age;
return _cout;
}
int main() {
Person p10(10);
cout << p10 << endl;
}
友元类
在类A中声明另一个类B为其友元类,则B中可以直接访问A类型对象的私有成员。
cpp
class Birthday
{
//声明Person类为该类的友元类
friend class Person;
public:
Birthday(int year = 2020) {}
private:
int _year;
};
class Person
{
public:
explicit Person(int age=18) :_age(age) {}
void ShowBirthday() {
// 在Person类中可直接访问Birthday的私有成员
cout << _bir._year<<endl;
}
private:
int _age;
Birthday _bir;
};
- 友元关系为单向的,B是A的友元,但A不是B的友元。
- 友元关系不具有传递性,B是A的友元,A是C的友元,但B不是C的友元。
- 友元关系不能继承。
内部类
如果一个类定义在另一个类的内部,就叫做内部类。内部类是独立的类,外部类没有任何优越的的访问权限。sizeof(外部类)也不会计算内部类。
内部类是外部类的友元类,但外部类不是内部类的友元类。