C++三五法则


Reference:

  1. c++三五法则(适合学完一遍不怎么清楚的童鞋)

1. 概念

当定义一个类时,我们显式隐式地指定了此类型的对象在拷贝、赋值和销毁时做什么。一个类通过定义三种特殊的成员函数来控制这些操作:拷贝构造函数拷贝赋值运算符析构函数

拷贝构造函数 定义了当用同类型的另一个对象初始化新对象时做什么,拷贝赋值运算符 定义了将一个对象赋予同类型的另一个对象时做什么,析构函数定义了此类型的对象销毁时做什么。我们将这些操作称为 拷贝控制操作

由于拷贝控制操作是由这三个特殊的成员函数来完成,所以我们称此为 C++三法则。而在较新的 C++11 标准中,为了支持移动语义,又增加了 移动构造函数移动赋值运算符,这样共有五个特殊的成员函数,所以又称为 C++五法则-------也就是说,“三法则”是针对较旧的 C++89 标准说的,“五法则”是针对较新的 C++11 标准说的。为了统一称呼,后来人们干脆把它叫做“C++三/五法则”。

法则一:需要析构函数的类也需要拷贝构造函数和拷贝赋值函数

通常,若一个类需要析构函数,则代表其默认的析构函数不足以释放类所拥有的资源,其中最典型的就是指针成员(析构时需要手动去释放指针指向的内存)。若存在自定义(且正确)的析构函数,但使用默认的拷贝构造函数,那么拷贝过去的也只是指针,此时两个对象的指针变量同时指向同一块内存,指向同一块内存的后果很有可能是在两个对象中的析构函数中先后被释放两次。所以需要额外的拷贝控制函数去控制相应资源的拷贝。

所以这类例子的共同点就是:一个对象拥有额外的资源(指针指向的内存),但另一个对象使用默认的拷贝构造函数也同时拥有这块资源。当一方对象被销毁后,析构函数释放了资源,这时另一个对象便失去了这块资源(但程序员还不知道)。

class person
{
public:
    std::string *name;
    int age;
    
    person(const char* the_name, int the_age){
        name = new std::string(the_name);
        age = the_age;
    }
 
    ~person(){
        delete name;
    }
};
 
 
int main(void)
{
    person a("me", 20);
    person b(a);
    std::cout << *b.name << std::endl;
 
    return 0;
}

在上面的代码中对象 b 使用默认的拷贝构造函数拷贝对象 a 的值,这个程序没有什么实际意义。在 main 函数返回时,a、b变量会分别被析构,它们的成员 name 指向同一块内存,所以在程序结束时便会发生错误

法则二:需要拷贝操作的类也需要赋值操作,反之亦然

需要拷贝操作代表这个类在拷贝时需要进行一些额外的操作。赋值操作=先析构+拷贝,所以拷贝需要的赋值也需要。反之亦然。

法则三:析构函数不能是私有的

如果类的析构函数是私有的,那么成员便无法销毁。所以在程序中不能定义这个类的对象。可以动态分配该对象并获得其指针,但无法销毁这个动态分配的对象(delete 失效)。若上面的类的定义是:

class person
{
public:
    std::string *name;
    int age;
 
 
    person(const char* the_name, int the_age)
    {
        name = new std::string(the_name);
        age = the_age;
    }
private:
    ~person()
    {
        delete name;
    }
};

若仍使用法则一的main函数,其中定义变量 a,b 会发生编译错误,然而,以下定义却可以通过编译 :

person *p; 
p = new person("me", 20)

但是,这样动态分配的变量是不能被释放的,在调用 delete p 会发生编译错误, 内存泄露就这样发生了。

法则四:如果一个类有私有的或不可访问的析构函数,那么其默认和拷贝构造函数会被定义为私有的

如果没有这条规则,可能会创造出无法被私有的对象。 理论上来说,当析构函数不能被访问时,任何静态定义的对象都不能通过编译器的编译,所以这种情况只会出现在与动态分配有关的拷贝/默认构造函数身上。

法则五:如果一个类有const或引用成员,则不能使用默认的拷贝赋值操作

原因很简单,const 或引用成员只能在初始化时被赋值一次,而默认的拷贝赋值操作会对所有成员都进行赋值。显然,它不能赋值 const 和引用成员,所以默认的拷贝构造函数不能被使用,即会被定义为私有的。

  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泠山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值