C++人该知道的N个问题与做法:了解C++类偷偷干的那些事,如何拒绝它

了解C++默默编写并调用哪些函数

当你创建一个类,但是如果你自己没声明其中一些东西,编译器就会为它声明(编译器版本的)一个拷贝构造函数、一个 copy 操作符和一个析构函数。此外如果你没有声明任何构造函数,编译器也会为你声明一个 默认构造函数。所有这些函数都是 public且 inline。

例:当你写下class Empty{  };

就相当于你写下了:

Class Empty 
{
 public:
    Empty() {...}                  // 默认构造函数
    Empty(const Empty& rhs){...}   //拷贝构造函数
    ~Empty(){...}                  //析构函数
    Empty& operator=(const Empty& rhs){...}   // copy 操作符.
};

当这些函数被调用的时候,它们才会被编译器创建出来,如下代码可以使上述每个函数均生成;

Empty e1;        //默认构造函数(销毁调用析构)
Empty e2(e1);    //拷贝构造
e2 = e1;         //拷贝构造符

如果你的类中写了构造函数,但是没有拷贝构造等,编译器会自动给你创建拷贝构造等,但一般来说只有代码合法且可以证明其有意义的才会被生成。万一这两个条件中有一个不符合,则编译器会拒绝生成operator=;

例:成员变量是一个引用

template<class T>
class Name
{
 public:
    Name(std: strings name, const T& value);    //并未声明 operator
 private:
    string& namevalue;
    const T objectvalue;
};

现在考虑下面会发生什么事?

string new("new");
string old("old");
Name<int>p(new,2);
Name<int>s(old,3);

p = s;

刚开始p.name和s.name都指向各自的string对象,当p=s之后,p.name是指向s.name的那个string吗?  

当然不是,因为C++不允许让引用改指向不同的对象。所以最好的方法就是自己定义。

如何拒绝让编译器自动生成

假设有有一个叶子类:class Leaf {...}

世界上没有两片完全相同的叶子,因此我们认为,为Leaf的对象进行拷贝是没有意义的,因为你无法复制一个独一无二的东西,所以你想让以下代码无法编译通过:

Leaf L1;
Leaf L2;
Leaf L3(L1);   //企图拷贝L1
L1 = L2;       //企图拷贝L2;

通常如果你不希望 class支持某特定功能,只要不声明对应函数就是了。但这个策略对copy构造函数和copy操作符却不起作用,因为上面已经说明,如果你不声明它们,当尝试调用它们时,编译器会为你声明它们。

这把你逼到了一个困境。如果你不声明copy构造函数或 copy操作符,编译器可能为你产出一份,于是你的class支持copy,如果你声明它们,你的 class还是支持 copy,但你的目的却是要阻止 copy。

一个地方可以入手,因为所有编译器生成的函数都是 public.为阻止这些函数被创建出来你得自行声明它们,但这里并没有什么需求使你必须将它们声明为 public.因此你可以将copy构造函数或 copy操作符声明为 private.明确的声明一个成员函数来阻止编译器暗自创建其专属版本,并且令这些函数为 private,阻止调用它。

一般而言这个做法也并不绝对安全,因为 member函数和 friend函数还是可以调用你的 private函数。除非你够聪明,不去定义它们,那么如果某些人不慎调用任何一个,会获得一个连接错误( linkage error)。

实现方式:

class Leaf
{
 public:
    ...
 private:
    Leaf(const Leaf& );    //参数名称并非必要,只不过大家习惯写出来
    Leaf& operator=(const Leaf& );   //只声明
};

其他做法:

将连接期错误移至编译期是可能的(而且那是好事,毕竟越早侦测出错误越好),只要将copy构造函数和 copy操作符声明为 private就可以办到,但不是在 Leaf自身,而是在一个专门为了阻止 copying动作而设计的 base class,这个 base class 非常简单:

class Uncopyable
{
 protected:        //允许对象构造和析构
    Uncopyable() { }
    ~Uncopyable() { }
 private:
    Uncopyable(const Uncopyable& );    //但阻止 copy
    Uncopyable& operator=(const Uncopyable& );
};

为求阻止Leaf对象被拷贝,我们可以做的就是继承 Uncopyable:

class Leaf: private Uncopyable {...};
//class不再声明copy构造函数或copy操作符

总结:

编译器可以暗自为 class 创建 default构造函数、copy构造函数、copy操作符,以及析构函数。

为阻止编译器自动提供的功能,可将相应的成员函数声明为 private并且不予实现。或使用像 Uncopyable这样的 base class也是一种做法;

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿尔兹

如果觉得有用就推荐给你的朋友吧

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

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

打赏作者

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

抵扣说明:

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

余额充值