class/struct
在cpp面向对象编程中,一般使用class来作为OOP的载体,而将struct仅作为类型的一个集合。虽然这两者在功能上基本没有差异,除了class的默认访问控制是private,而struct的是public的。
类的成员
类作用域
类本身是一个作用域,我们可以在类内声明一个函数/变量,并且用类名::函数名的方式在类外定义它。
特别的,对于类的静态变量而言,我们必须这么做。
静态成员
对于类来说,静态成员并不依赖于类的实例而存在,也就是说,它的静态成员会存在于整个程序的执行过程中,直到程序结束。因此,类的所有实例可以通过类名::成员名的方式来共享类的所有静态成员,且静态函数只能调用静态成员。
const成员函数
如果一个类指针是const的,那么它不能修改这个类中的任何成员变量。
如果成员函数在参数列表之后紧跟着一个const,表示这是一个常量成员函数,默认情况下,this是一个指向非常量类型的常量指针(可以看作是const在后的指针),这里const的作用是修改隐式this指针的类型,使其变成一个指向常量的指针。
构造函数
如果我们使用new运算符新建一个对象,那么它首先会调用malloc分配内存,然后调用构造函数创建这个对象。
如果类有继承层次,那么首先执行基类的构造函数,再执行派生类的。cpp没有super关键字,也就是说,我们无需在派生类的构造函数中显式的调用基类构造函数,这个调用关系由编译器来管理。
构造函数不能是const:显然类内成员需要初始化
构造函数不能是virtual:
- 对象创建时,虚表还没有构造完成
- 构造函数有其调用次序,virtual显然会破坏这种原则
每个类都有一个默认的构造函数,自定义构造函数会覆盖默认的构造函数,如果我们需要保留默认构造函数,则可以将其声明为default:A() = default
构造函数必须是public的,但不能通过类指针进行调用。
初始值列表构造函数:很方便的一种赋初值形式,甚至可以在赋完初值后再执行一些额外的操作
class A {
int x;
double y;
char z;
public:
A(int a, double b, char c) : x(a), y(b), z(c) {}
};
委托构造
允许一个构造函数调用另一个构造函数,例如:
class A {
public:
A() : A(0) {}
A(int x) { std::cout << x << std::endl; }
}
这样,第一个构造函数就调用了第二个构造函数。
拷贝构造
拷贝构造函数默认只一一复制非静态成员。
原型:A(const A& other)
,有以下四种场景会触发:
- 用一个对象初始化另一个对象时,如
A x; A y = x
- 通过值进行传参
- 通过值返回对象
- 用花括号列表初始化类成员
拷贝赋值运算符
实际上是重载赋值号,和拷贝构造的区别是,被赋值的对象是已经创建好的。原型:A& operator=(const A& other)
移动构造
原型:A(const A&& other)
移动赋值运算符
原型:A& operator=(const A&& other)
析构函数
与构造函数正好相反,先派生类后基类。
基类的析构函数需要声明为虚函数,考虑以下场景:
Base* ptr = new Derived();
delete ptr;
执行delete ptr;
时,如果基类的析构函数非虚,采用静态绑定方式,会直接调用Base类的析构函数。
如果基类的析构函数为虚,那么编译器看到虚函数会采用动态绑定方式,发现指针指向的类型实际上是Derived,由于Derived类的析构函数重写了Base类的析构函数(为什么不同名能重写,问就是特性),而且Derived的析构函数调用完成后会默认调用Base的析构函数,这样就实现了正常的资源释放。
析构函数一般不显式调用,一种特例是使用placement new(一种可以在自己申请的内存上创建对象的方法)创建对象时,由于对象不一定在堆空间上,此时不能调用delete释放资源,而需要手动调用析构函数。
标签:调用,const,函数,成员,析构,cpp,构造函数 From: https://www.cnblogs.com/DGJG/p/17936912