您当前的位置: 首页 > 

我什么都布吉岛

暂无认证

  • 4浏览

    0关注

    292博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

类拷贝规则——三/五法则

我什么都布吉岛 发布时间:2020-09-17 11:01:33 ,浏览量:4

三/五法则是什么?

早些时候,C++对拷贝行为的管理需要通过三个函数完成,其分别是:

  • 拷贝构造函数
  • 拷贝赋值函数
  • 析构函数

三法则,一析两拷。

class A
{
public:
	A() { }
	A& operator=(const A&) {} //rule1
	A(const A&) {}            //rule2
	~A() {};                  //rule3
private:
};

这就叫做“三法则”,随着语言的发展,增加了两个函数,用于更精细的拷贝构造:

  • 移动构造函数
  • 移动赋值运算符

扩充之后的拷贝法则被称为“五法则”。

三五法则让编译器承担部分规则定义

事实上我们不需要定义所有的五个法则,如果你没有定义的话,使用语法:规则=default让编译器按照其内置规则补充未明显定义的法则。如,让系统生成拷贝赋值规则:

A & A(const &A)=default;

如,拷贝构造函数,按顺序复制原对象的所有数据成员。别人做的事情(编译器默认行为)当然不如自己做的,可能出现内存泄漏、值非预期等问题。

为了降低拷贝资源上出现的问题,我们有以下trick:

  • 如果需要定义析构函数,那么它肯定需要拷贝构造和拷贝赋值;
  • 如果需要拷贝构造函数,那么也需要赋值构造函数,不一定需要析构函数;

对于第一个trick,一个类在什么情况下需要定义析构函数?动态分配一个资源时就会需要定义析构函数,因为合成析构函数并不会对你动态分配的内存进行delete操作,那为什么一定要定义好两种拷贝行为?假设我们有一个自定义的类HasPtr,查看下面例子:

HasPtr f(HasPtr hp)
{
	HasPtr ret=hp;//拷贝给定的HasPtr
	return ret;   //ret和hp都被销毁,删除了同一块内存
}
int main()
{
	HasPtr a;
	HasPtr b=a;//a b都含有同一个动态资源
}
//b先离开,删除了这个共有资源
//a再离开,再次删除同一个资源

假设在析构函数中进行了动态分配内存的delete,函数参数hp和ret都会在f返回时进行析构,也就是进行了两次delete。

对于第二个trick,如果一个类需要控制拷贝构造行为,那么意味着它必然需要一并控制拷贝赋值行为(例子,独一无二的ID号),因为没有用到动态内存,因此完全不需要析构函数。

三五法则指定禁止部分规则

假如我们需要禁止拷贝构造规则,可以这么写

A (const & A )=delete;

使用规则=delete禁止用户调用此项规则。当用户创建对象时,使用类似以下语句:

A a;
A b(a);//error,因为被删除了

同理,我们也可以对析构函数进行delete声明。在此之前,这样的禁止是通过私有化该规则完成的。

~A()=delete;

如果不能调用析构函数,那么编译器将会禁止包括临时在内的对象生成。但是我们可以通过new语句生成这个对象,但是又不能是使用delete释放new指针对应的内存,这个时候就需要额外定义内存回收函数,以防止内存泄漏。

编译器合成的三五法则可能是删除的
  • 一个类下的类成员析构函数是删除的,那么该类析构函数也会受影响为删除的;
  • 一个类下的类成员拷贝构造函数是删除的,那么该类拷贝构造也为删除的;
  • 一个类下的类成员赋值运算符是删除的,那么该类的赋值运算符也是删除的;
  • 一个类下的类成员析构函数是删除的,那么该类的默认构造函数是删除的;
  • 一个类下引用没有初始化器(默认初值),该类默认构造函数是删除的;
  • 一个类下const对象没有初始化器(默认初值),该类默认构造函数是删除的。

简单来说,一个类有成员对应的三法则若为删除,那么合成的三法则也是删除的;如果一个类成员引用、const对象没有给定初始值,那么该类的默认构造函数是删除的。

关注
打赏
1658157489
查看更多评论
立即登录/注册

微信扫码登录

0.0370s