c++11 中函数声明 新关键字 delete的妙用之一: 搭配宏NonCopyable(ClassName) 使用

来源:互联网 发布:迅捷数据恢复注册问题 编辑:程序博客网 时间:2024/06/05 22:47

c++11 中函数声明 新关键字 delete的妙用之一搭配宏NonCopyable(ClassName) 使用

编译器版本: gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) 

 

1.  首先先来见识一下 c++11 声明中的新关键字 delete

 使用如下代码先来试试水.

#include <string>class  Person{public:    Person(std::string name) : name_(name) { }    Person(const Person &name) = delete;private:    std::string name_;}; int main(int argc, char **argv){    Person p1("Crane");    Person p2(p1);}


首先这是一个Person.借用莱布尼茨一句老掉牙的话: "世界上没有两片完全相同的叶子,也没有性格完全相同的人。

体现在Person类中就是不允许任何两个用户的名字相同,  首先从根源上杜绝禁止Person类的拷贝构造函数和赋值运算符.

这里使用 C++11 成员函数声明新特性: = delete,  从逻辑语义上禁止了对于 Person类拷贝构造函数和赋值运算符

 

使用g++ 命令编译

g++  -std=c++11         -g  -Wall            main.cpp   -o main

编译错误如下:  ( 记得开启 编译器 C++11 支持 )


main.cpp: In function ‘int main(int, char**)’:
main.cpp:37:17: error: use of deleted function ‘Person::Person(const Person&)’
     Person p2(p1);
                 ^
main.cpp:26:5: note: declared here
     Person(const Person &) = delete;

 

编译器明确提示了 在main函数中使用了 deleted function.  并且给出了行号,  错误原因一目了然用户看到这样的错误信息很快可以定位到问题所在.

并且注意一点:   " = delete "  函数声明和成员函数的作用域已经无关了, 既可以放在private域中也可以放在public 域中.

2. 在 delete没有出现之前的解决方案:

 看完了“ = delete” 声明禁止某些函数使用的方式。 再来回想一下在没有 = delete 的年代里, 如何禁止类的拷贝构造函数和赋值运算符的使用。

  简单来说, 就是将禁止使用的成员函数其声明为private类型: 将拷贝构造函数和赋值运算符放到private域中。

  这样也是可以工作的,boost::noncopyable就是基于private 这样的方式干的。

3. 用宏来实现NonCopyable的功能通过private实现.

如果我有很多类,都禁止拷贝的情况下......使用private来实现noncopyable.

在我的项目中有很多资源管理类这些类都使用RAII的手法来管理资源这些类很多都是不可以拷贝构造的.

比如Scoped_Mutex,  Scoped_Curson,  Scoped....

 

于是乎代码如下

class Scoped_Mutex{private:    Scoped_Mutex(const Scoped_Mutex &) ;    Scoped_Mutex & operator =(const Scoped_Mutex &) ;}; class Scoped_Cursor{private:    Scoped_Cursor(const Scoped_Cursor &) ;    Scoped_Cursor & operator =(const Scoped_Cursor &) ;};.... 


 

这样每个类中都要写这些倒人胃口的private 函数声明, 并且和程序逻辑没有半毛钱关系如果是这样或许为了偷懒我就不写了.

那么问题来了既然boost已经实现了 boost::noncopyable, 直接用自己的类继承 boost::noncopyable不就好了吗

当然在对boost依赖较大时可以这样但是在我的源码中如果只是因为noncopyable, 就去使用boost. 真是划不来.

并且如果我的源码是用来提供基础库服务的这样每一个使用我的服务的上层应用都要强制安装boost. 用户会觉得: oh, shit !

所以如果没有足够的对于boost的需求积累在代码里就不会轻易使用boost的功能.毕竟这不算是一个轻量级” 的家伙.

  

但是对于上面那坨重复的东西我又实在是太恶心乏味了该我们的宏NonCopyable 出场了如下: ( 首先我们不使用delete 版本的NonCopyable)

 

// 示例:  NonCopyable(Scoped_Cursor) 这样的宏必须使用放在类的private区域.#define  NonCopyable(ClassName)   ClassName(const ClassName &) ;  \                                  ClassName & operator =(const ClassName &)  class Scoped_Mutex{private:    NonCopyable(Scoped_Mutex);}; class Scoped_Cursor{private:    NonCopyable(Scoped_Cursor);};


 

那么这段代码写好了注意我们没有在NonCopyable宏定义前添加private 关键字所以我们要手动把NonCopyable(Scoped_Cursor) 这样的宏使用放在类的private区域并且要把 NonCopyable(ClassName) 处的注释写好

以防用户不将 NonCopyable(Scoped_Cursor)  放置在private区域中.

 

自然对于用户在哪里使用NonCopyable 限制越多,说明NonCopyable越不易用. 用户或许不看注释根据NonCopyable的字面意思以为NonCopyable放置在任何域中都可以使NonCopyable逻辑生效.

 

那么好为了防止用户误用让我们在我们的NonCopyable(ClassName)宏 中添加private: 字样吧.

#define  NonCopyable(ClassName)   private: \                                  ClassName(const ClassName &) ;  \                                  ClassName & operator =(const ClassName &)


 

这次可以愉快的( 肆无忌惮的 )使用我们的 NonCopyable宏了小试一下似乎乍一看很好很愉快. 世界很美好

class  Person{public:    Person(std::string name) : name_(name) { }    NonCopyable(Person);     std::string GetName() {        return name_;    }private:    std::string name_;};


 

 NonCopyable(Person) 到底放在private, 还是public区域中用户不用再关心了当然没有坑是不可能的. 请看下文 !

这时候我们在main() 函数中使用NonCopyable(Person) 后面定义的GetName(), 编译器说这是一个private 函数没有使用权限.  神马??? 一脸懵逼我明明把 GetName() 放在了public 域中的.

再回头去看看我们的NonCopyable(ClassName) 定义因为NonCopyable中自带了private 魔性, NonCopyable 后面的函数声明如果没有添加作用域关键字也就魔性的被连累了

于是赶紧去NonCopyable 宏的定义处添加一句注释: NonCopyable 后面如果有public 函数声明请显示使用public: 声明!  ( 这是什么鬼东西感觉还不如之前不自带private: 版本的NonCopyable, 于是又替换回之前的版本)

 

一圈下来,  因为private 的问题程序猿哥哥的内心是泪崩的.

4. 于是 delete 版本的 NonCopyable 横空出世只为我心中完美的程序猿......的程序.

#define  NonCopyable(ClassName)   ClassName(const ClassName &) = delete;  \                                  ClassName & operator =(const ClassName &) = delete

这次终极版的 NonCopyable(ClassName) 定义中我们不要见鬼的private : 而是使用了 = delete 声明.

因为我们之前说过 = delete 可以使用在任何作用域中 !!!

所以我们的NonCopyable(ClassName) 不再有只能在private域中使用的限制了.

终于不用再担心我的copyable鬼畜bug.


阅读全文
1 0