【Effective C++ 条款05-07 笔记】【C++默默编写并调用哪些函数】【禁止编译器自动生成函数】【什么时候基类要带虚析构函数】

条款05:了解C++默默编写并调用哪些函数

一个空类,编译器会自动声明:

  • 默认构造函数(对于非空类:只在没有自定义任何构造函数的时候,才会由编译器补充)
  • 拷贝构造函数
  • 拷贝赋值运算符
  • 析构函数(非虚函数)

注意:当一个class内含有reference/const成员时,编译器不提供拷贝赋值运算符的补充,只能由程序员自己编写

这是因为C++不允许改变reference成员的指向,也不允许更改const成员

条款06:若不想使用编译器自动生成的函数,就该明确拒绝

  • 问题:

比如说想要禁止一个类对象的拷贝操作,就要禁止拷贝构造函数和拷贝赋值运算符。但是不声明这两个函数,编译器可能会自动生成;要想避免编译器自动生成,又要自己声明一份;怎样都避免不了产生这两个函数。

  • 解决方案1:

将拷贝构造函数、拷贝赋值运算符声明为private函数,这样在类外部不能以拷贝构造和复制的形式公然调用这两个函数

class A {
    
    
public:
    A() = default;
private:
    A(const A&);
    A& operator=(const A&);
};
  • 解决方案1存在的问题:

以class A为例,A的其他成员函数和友元函数还是能调用class A的private函数。由此引出解决方案2

  • 解决方案2:

定义一个基类专门阻止拷贝动作

class unCopyable {
    
    
protected:
    unCopyable() {
    
    }
    ~unCopyable() {
    
    };
private:
    unCopyable(const unCopyable&) {
    
    }
    unCopyable& operator=(const unCopyable&) {
    
    }
};

class A : private unCopyable{
    
    
public:
    A() = default;
};

此时,就算是class A的成员函数和友元函数,在尝试拷贝A的对象时,编译器会“试着“生成拷贝构造函数和拷贝赋值运算符,但是这种”试着“会被基类阻止,因为基类的对应函数时private,子类不能调用

条款07:为多态基类声明virtual析构函数

当一个基类指针指向一个子类对象时,若基类的析构函数不是virtual函数,那么在delete基类指针的时候,只会释放基类对象的资源,不会释放子类对象的资源,从而造成内存泄漏

任何带有virtual函数的class都应该有一个virtual析构函数

虚函数的实现原理

  • 每个含有virtual函数的class都有一个vtbl(虚函数表),vtbl中存储的是指向各个虚函数的函数指针;

  • 每个对象内存空间内存在一个vptr(虚指针),这个vptr指向类的vtbl

  • 当对象调用某个virtual函数的时候,编译器根据对象的vptr找到类的vtbl,在vtbl中寻找适当的函数指针

注意:不作为基类使用的class不要声明析构函数为virtual函数,因为虚表和虚表指针会占用额外的内存;同时,std::string和STL容器的析构函数都是non-virtual,不要作为基类使用

猜你喜欢

转载自blog.csdn.net/weixin_44484715/article/details/121364719
今日推荐