如果想让你的类定义出来的对象是独一无二的,即对象无法被复制,或者使用赋值操作符赋给另外一个对象,那么最好的方法就是禁用拷贝构造函数和赋值操作符。下面介绍几种禁用的方法。(方法来自Effective C++,如果想禁用类的其他函数,方法类似)

 

1. 定义为private且不实现它

我们知道,拷贝构造函数和赋值操作符重载函数,即使不定义,编译器也会生成一个默认的函数。但是如果定义了,class还是会支持这两个函数。那么应该怎么去禁用它们呢?

在C++中,如果不想让对象调用某个方法,那么可以将这个方法声明为private,因此,我们可以将拷贝构造函数和赋值操作符重载函数声明为private,就可以阻止别人调用它了。但是,private函数还是可以在类的成员函数和friend函数中调用。这里还有一个技巧,就是将函数定义为private而且不实现它。

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

将SpecialClass这样定义,并且不实现这两个函数,那么如果有人试图拷贝这个类的对象,编译器就会报错:

SpecialClass sc1;
SpecialClass sc2(sc1); //error, 编译出错
SpecialClass sc3;
sc3 = sc1; //error, 编译出错

而且即使在成员函数或者friend函数中调用了它,链接时也会出错。

 

2. 继承Uncopyable类

第一种方法可以解决禁用拷贝构造函数的问题,但是还有一点不完美的是,如果用户不小心在成员函数或者friend函数中调用了它,那么这个错误只有到链接阶段才能被发现。所以就有了继承一个Uncopyable类的方法:

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

此时,只需要继承Uncopyable类就可以防止其对象被拷贝了。其原理是:SpecialClass继承了Uncopyable函数,它的对象如果调用了拷贝构造函数,或者使用了赋值操作符,那么编译时,就会去调用基类的对应函数,但是基类里的这两个函数是private的,所以编译会失败。

另外,boost也提供了一个Uncopyable函数,那个class名字为noncopyable,因此我们也可以这样做:

#include <boost/utility.hpp>

class SpecialClass : boost::noncopyable {
...
};

 

3. C++0x中的新方法

在C++0x中,新添加了两个关键字:default和delete,可以用于控制某个类使用默认函数(default)或者禁用某个函数(delete)。其中,使用default关键字可以将某个函数定义为默认函数。如:

class SpecialClass {
...
public:
SpecialClass(const SpecialClass&) = default;
SpecialClass& operator=(const SpecialClass&) = default;
};

可能这样写有点多余,但是它可以直观表示“我使用的默认的函数”。而且,如果有人想显式定义默认函数,但是自己写的代码又可能有错误,就可以使用这个关键字,方便且不出错。

delete关键字可以禁用掉类的方法,正如我们上面前两节所做的事情,就可以用这一个关键字完成:

class SpecialClass {
...
public:
SpecialClass(const SpecialClass&) = delete;        //禁用拷贝构造函数
SpecialClass& operator=(const SpecialClass&) = delete;
};

default关键字可以用于一切含有默认方法的函数。delete关键字可以用于类的所有方法,例如可以禁用掉函数的某些类型的参数:

class SpecialClass {
...
public:
void fn(long);
void fn(int) = delete;
}; SpecialClass sc;
long l;
sc.fn(l); //ok, 调用void fn(long);
int i;
sc.fn(i); //error, 调用void fn(int);

上面的代码会将void fn(int)类型的函数禁用掉,而如果不禁用的话,sc.fn(i)会进行隐式转换调用void fn(long)函数。

 

参考:

C++11 FAQ

 

 

 

文章摘自 xd_xiaoxin博文。

禁用编译器自动生成的函数(Effective C++之06)的更多相关文章

  1. Effective C++ 之 Item 6 : 若不想使用编译器自动生成的函数,就该明确拒绝

    Effective C++ chapter 2. 构造 / 析构 / 赋值运算 (Constructors, Destructors, and Assignment Operators) Item 6 ...

  2. 【Effective c++】条款6:若不想使用编译器自动生成的函数就应该明确拒绝

    地产中介卖的是房子,其使用的中介软件系统应该有个类用来描述卖掉的房子 class HomeFoeSale { ......} 但是任何房子都是独一无二的,不应该存在两个房子拥有同样的属性,因此以下操作 ...

  3. Effective C++_笔记_条款06_若不想使用编译器自动生成的函数,就该明确拒绝

    (整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 通常如果你不希望class支持某一特定机能,只要不声明对应函数就 ...

  4. 读书笔记 effective c++ Item 6 如果你不想使用编译器自动生成的函数,你需要明确拒绝

    问题描述-阻止对象的拷贝 现实生活中的房产中介卖房子,一个服务于这个中介的软件系统很自然的会有一个表示要被销售的房屋的类: class HomeForSale { ... }; 每个房产中介会立刻指出 ...

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

    为驳回编译器自动提供的功能,可将相应的成员函数声明为private并且不予实现. 使用像Uncopyable这样的base class也是一种做法(即先声明一个基类,然后私有继承它).这其实有点像使用 ...

  6. Effective C++学习笔记 条款06:如不想使用编译器自动生成的函数,就该明确拒绝

    一.为驳回编译器自动提供的机能,可将相应成员函数声明为private并且不予实现.(如果你仅仅是自己不实现的话,编译器会帮你实现) 如: class A { public: A(const strin ...

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

    规则一 将成员函数声明为private而且故意不实现他们 class HomeForSale { public: ... private: ... HomeForSale(const HomeForS ...

  8. Effective C++ 条款六 若不想使用编译器自动生成的函数,就该明确拒绝

    class HomeForSale //防止别人拷贝方法一:将相应的成员函数声明为private并且不予实现 { public: private: HomeForSale(const HomeForS ...

  9. C++如何拒绝编译器自动生成的函数

    每一个class,编译器都会自动生成四个特殊成员函数: destructor(析构函数) default constructor(默认构造函数) copy constructor(copy构造函数) ...

随机推荐

  1. java servlet的工作原理

    servlet本质上就是java类嘛.不过是有特殊规范的java类而已.下面就说一说为什么servlet要有特殊规范. 首先,考虑一下什么地方用servlet,WEB应用,而且是需要servlet容器 ...

  2. 【GoLang】50 个 Go 开发者常犯的错误

    1. { 换行:   Opening Brace Can't Be Placed on a Separate Line 2. 定义未使用的变量:  Unused Variables 2. import ...

  3. 64位系统运行32位Oracle程序解决方案

    Attempt to load Oracle client libraries threw BadImageFormatException. This problem will occur when ...

  4. 从其它系统登录到SharePoint 2010系统的单点登录

    以前做的只是使用SharePoint的单一登录,用SharePoint去登录其他的系统,现在要反过来,用Form认证的系统来登录SharePoint. 我们都知道,SharePoint使用的是域认证系 ...

  5. Javascript 面向对象

    面向对象语言的标识:类的概念. ECMAScript中没有类的概念,因此他的对象与基于类的语言中的对象有所不同. ECMA-262把对象定义为:无序属性的集合,其属性可以包含基本值.对象.函数.我们可 ...

  6. delphi XE5下安卓开发技巧

    delphi XE5下安卓开发技巧 一.手机快捷方式显示中文名称 project->options->Version Info-label(改成需要显示的中文名即可),但是需要安装到安卓手 ...

  7. Django~Databases

    更换数据库 migrate 下哈 admin 要重新建立 修改\mysite\settings.py DATABASES = {    'default': {        'ENGINE': 'd ...

  8. codeforces 558B. Amr and The Large Array 解题报告

    题目链接:http://codeforces.com/problemset/problem/558/B 题目意思:给出一个序列,然后找出出现次数最多,但区间占用长度最短的区间左右值. 由于是边读入边比 ...

  9. Luncence .Net 使用

    public partial class Form1 : Form { public Form1() { InitializeComponent(); } //标准分词 private void bu ...

  10. java课后作业6

    一.运行TestInherits.java 结论:通过super调用基类构造方法,必须是子类构造方法中的第一个语句. 二.为什么子类的构造方法在运行之前,必须调用父类的构造方法?能不能反过来?为什么不 ...