点此到文末惊喜↩︎
一个类
中只能
出现一个默认构造函数
无参的
或所有形参都有缺省值
类中有类
:类A含有类B的对象作为成员,并且类B显式定义了默认构造函数,则定义类A对象的时候编译器会产生一个默认构造函数,并在这个默认构造函数中提供了调用类B构造函数的代码
。类继承类
:类B继承于类A,且类A显式定义了构造函数,那么在生成类B对象的过程中编译器同样会产生一个默认构造函数,在默认构造函数中提供调用基类A构造函数的代码
含虚函数类
:某个类含有虚函数,那么编译器会自动产生一个默认构造函数进行虚表指针相关的初始化操作
虚继承基类
:一个类虚继承于其他类,那么同样的编译器会为该类产生默认的构造函数。使用实参赋值
:在创建对象时,使用实参为对象的成员属性赋值,由编译器自动调用防止隐式转换
:如果是单参构造函数,需要使用explicit
进行修饰类中有指针
成员时,由系统默认创建的复制构造函数会存在“浅拷贝”的风险
,因此必须显式自定义复制构造函数。形参为类对象本身的引用
,根据调用对象进行深拷贝而构建新的类对象// 1. 对象的赋值初始化
Complex c2(c1);
Complex c2 = c1;
// 2. 函数形参
void Function(Complex c){
...
}
// 可以使用const & :确保实参的值不会改变,并避免复制构造函数带来的深拷贝开销
void Function(const Complex & c){
}
//3. 函数返回值
Complex Func() {
Complex a(4);
return a;
}
移动语义
,将已构建的对象内存所有权转移
给新对象,避免了复制语义下,对旧对象的一次拷贝和销毁开销。形参是右值引用
,可以使用move将左值进行转换。 class Integer {
private:
int* m_ptr;
public:
Integer(Integer&& source)// 注意形参是右值引用
: m_ptr(source.m_ptr) {// 指针成员需要进行赋值
source.m_ptr= nullptr;
cout << "Call Integer(Integer&& source)移动" << endl;
}
};
int main(int argc, char const* argv[]) {
Integer a;
Integer b(std::move(a));// 将a转换成右值引用
return 0;
}
class Base {
public:
int value1;
int value2;
Base() //目标构造函数
{
value1 = 1;
}
Base(int value) : Base() //委托构造函数
{ // 委托 Base() 构造函数
value2 = value;
}
};
void EntrustedConstruction()
{
Base b(2); //首先调用Base(int value) : Base() 毫无疑问
//然后会走到base()中,先给value1复制,然后走到Base(int value) : Base() ,给value2赋值
std::cout << b.value1 << std::endl;
std::cout << b.value2 << std::endl;
}
class Student {
public:
//1. 默认构造函数,没有参数或形参具有缺省值
Student(int i=2){
this->age = 20;
this->num = 1000;
};
// 2. 初始化构造函数,有参数和参数列表
Student(int a, int n):age(a), num(n){};
// 3. 拷贝构造函数,参数是对象
Student(const Student& s){
this->age = s.age;
this->num = s.num;
};
// 4. 移动构造函数,参数是右值引用
Student(const Student&& s){
this->age = s.age;
this->num = s.num;
};
// 3. 转换构造函数,将int型转换成类对象
Student(int r){ //
this->age = r;
this->num = 1002;
};
~Student(){}
public:
int age;
int num;
};
分配内存空间后
进行的分配内存空间的同时(原地构造效率高)
,先于构造构造函数的执行class Test {
public:
Test(int a, int b, int c)
: _a(a) // 列表初始化
{ // 赋值初始化
_b = b;
_c = c;
private:
int _a;
int _b;
int _c;
};
引用成员
const修饰的成员
类类型的成员变量
,减少构造成本
class Derived : public Base {
public:
// 调用基类构造函数,而它拥有一组参数时,要使用成员初始化列表
Derived()
: Base("DerivedStr", 200) // 这个是正确的
{
//Base::Bstr = "DerivedStr"; // 基类构造函数再此之前调用,这里赋值没有用。
//Base::_i = 200;
cout << "Derived Constructor" << endl;
}
string Dstr;
};
虚基类
的构造函数(多个虚基类则按照继承的顺序执行构造函数)。基类
的构造函数(多个普通基类也按照继承的顺序执行构造函数)。成员类对象
的构造函数(按照声明顺序)派生类
的构造函数。显式定义拷贝构造函数并设置为private
,防止类外调用友元类可以调用类中的private成员函数
,如果该函数只声明不定义可能产生编译错误。所以可以通过将拷贝构造函数放在base基类的private中进行解决
1) Empty(); // 缺省构造函数
2) Empty( const Empty& ); // 拷贝构造函数
3) Empty& operator=( const Empty& ); // 拷贝赋值运算符
4) ~Empty(); // 析构函数
用unique_ptr对象来取代类的指针成员
,便对构造函数做了强化,免除了抛出异常时发生资源泄漏的危机,不再需要在析构函数中手动释放资源default
:为类内函数显式声明default,可以让编译器必须生成该函数的默认版本
delete
:为类内函数显式声明delete,可以让编译器阻止生成该函数的默认版本
=0
:表示该vitual函数为纯虚函数。拥有纯虚函数的类是抽象类,不能进行实例化,但是在派生类中必须进行重写定义或向下传递
。class A {
public:
A() = default;// 显式要求编译器生成构造函数
A(int a){};
virtual ~A();
void* operator new() = delete;//这样不允许使用new关键字
void f1();
virtual void f2();
virtual void f3()=0;
};
清理类内指针成员
:系统无法自动释放指针变量指向的堆空间,需要在析构函数中使用delete进行处理派生类
的析构函数;成员类对象
的析构函数;基类
的析构函数。避免内存泄漏
:指向派生类对象的基类指针被delete时,如果基类析构函数不是虚函数,将只会调用基类的析构函数,无法释放派生类的申请的内存。一般不要将基类的析构函数定义为纯虚的
,纯虚析构函数会强制子类实现自己的析构函数,否则会链接失败只能通过new在堆上创建
,无法在栈上创建对象。因为在栈上创建的对象在生命周期结束后无法自动调用析构函数无法使用delete释放对象
,delete释放对象是通过调用析构函数实现的,当析构函数为private时,无法进行调用限制继承
,因为派生类无法使用析构函数,可以将析构函数设置为protected无法释放对象
,可以在类内增加一个public的destroy()
函数调用析构函数
点此跳转到首行↩︎