【读书笔记】Effective C++ 条款5~6:若不想使用编译器自动生成的函数,就该明确拒绝

目录

[一、C++ 默认生成的函数](#一、C++ 默认生成的函数)

二、何时会拒绝生成?

三、明确拒绝拷贝的方法

[1. 将拷贝函数放到 private 中(但不定义)](#1. 将拷贝函数放到 private 中(但不定义))

[2. 友元也无法绕过](#2. 友元也无法绕过)

四、通过继承实现(更优雅的方式)

[1. 定义 uncopyable 基类](#1. 定义 uncopyable 基类)

[2. 派生类继承 nocopy](#2. 派生类继承 nocopy)

[3. 原理](#3. 原理)


一、C++ 默认生成的函数

如果不显式声明,C++ 编译器会自动生成以下函数(都是 public 且 inline 的):

  • 默认构造函数

  • 拷贝构造函数

  • 拷贝赋值运算符

  • 析构函数

二、何时会拒绝生成?

在某些情况下,编译器可能拒绝生成默认的拷贝函数:

  1. 类中有 const 成员:const 成员不能被赋值,因此拷贝赋值运算符需要自定义

  2. 类中有引用成员:引用必须在初始化时绑定,不能被重新赋值

  3. 将拷贝函数放到 private 中:手动阻止生成和调用

这些情况不违背 C++ 规则,只是需要程序员自己定义合适的拷贝行为。

三、明确拒绝拷贝的方法

1. 将拷贝函数放到 private 中(但不定义)

cpp 复制代码
class hause {
public:
    hause(double s, const std::string& name) : _s(s), _name(name) {}

private:
    // 只声明不定义,阻止编译器自动生成
    hause& operator=(const hause&);
    hause(const hause&);

    double _s;
    std::string _name;
};

说明:

  • 每套房子都是唯一的,因此不允许拷贝

  • 将拷贝构造函数和拷贝赋值运算符声明为 private,阻止了外部调用

  • 只声明不定义 :如果成员函数或友元试图调用,会得到链接错误

2. 友元也无法绕过

cpp 复制代码
class hause {
    friend hause func(hause& h);  // 声明友元函数

public:
    hause(double s, const std::string& name) : _s(s), _name(name) {}
    std::string getname() { return _name; }

private:
    hause& operator=(const hause&);  // 只声明不定义
    hause(const hause&);

    double _s;
    std::string _name;
};

hause func(hause& h) {
    hause ret = h;  // 调用拷贝构造函数,只有声明没有定义
    std::cout << ret.getname() << std::endl;
    return ret;
}

结果:

  • 虽然 func 是 hause 的友元,可以访问 private 成员

  • 但拷贝函数只有声明,没有定义

  • 因此链接时找不到函数定义,产生链接错误

典型例子 :iostream 类家族就通过这种方式阻止拷贝。比如不能拷贝 cout、cin 对象。

四、通过继承实现(更优雅的方式)

1. 定义 uncopyable 基类

cpp 复制代码
class nocopy {
public:
    nocopy() {}                         // 允许构造和析构
    ~nocopy() {}

private:
    nocopy& operator=(const nocopy&);    // 只声明不定义
    nocopy(const nocopy&);
};

2. 派生类继承 nocopy

cpp 复制代码
class hause : public nocopy {
public:
    hause(double s, const std::string& name) : _s(s), _name(name) {}
    std::string getname() { return _name; }

private:
    double _s;
    std::string _name;
};

3. 原理

当尝试拷贝 hause 对象时:

  1. 编译器会尝试生成 hause 的拷贝构造函数

  2. 生成的拷贝构造函数会先调用基类 nocopy 的拷贝构造函数

  3. 但基类 nocopy 的拷贝构造函数是 private 且未定义

  4. 因此编译失败(如果是在成员函数内尝试拷贝,则产生链接错误)

优点:

  • 更清晰地表达意图:hause 类"不可拷贝"

  • 不需要在每个要禁止拷贝的类中重复声明 private 拷贝函数

  • 错误信息通常在编译期(而不是链接期)就能发现

相关推荐
冻柠檬飞冰走茶6 小时前
字母移动游戏题解:贪心求最长可保留连续子串(附 C 实现)
c语言·开发语言·数据结构·算法·动态规划
文丑颜不良啊6 小时前
LeetCode2161~2180题目解析
java·leetcode
FRing036 小时前
vue2干货总结7(自定义指令、插槽、单页应用程序、路由的使用(VueRouter))
开发语言·前端·vue.js·vue2
雪落漂泊6 小时前
C++算法 二分查找篇(上)
开发语言·c++·算法
波力海苔夹心脆6758 小时前
C# WPF 资源字典详解:ResourceDictionary、MergedDictionaries 与自定义控件案例
开发语言·c#·wpf·入门教程·resource·资源字典
不会算法的笨小孩8 小时前
异常的使用以及会出现的问题
开发语言·c++·c++11·异常
文丑颜不良啊8 小时前
LeetCode2141~2160题目解析
java·leetcode
LabVIEW开发8 小时前
LabVIEW 连接板:28 个端子怎么排,优先级怎么定
java·算法·labview
天天被压力9 小时前
【A股分钟级高频数据完全手册 #07】北交所历史分钟K线接口:京市Pro版分时批量
java·人工智能·python
天天被压力9 小时前
【A股分钟级高频数据完全手册 #08】股票日、分时数据接口对比:普通分时与Pro版差异实测
java·人工智能·python