若不想使用 compiler-generated functions(编译器生成函数)请明确拒绝

本文介绍如何在C++中通过将拷贝构造函数和拷贝赋值运算符声明为私有并避免定义来防止对象被拷贝,提供了一个Uncopyable基类的实例。
 
房地产代理商出售房屋,服务于这样的代理商的软件系统自然要有一个 class(类)来表示被出售的房屋:
class HomeForSale { ... };
 
每一个房地产代理商都会很快指出,每一件房产都是独特的——没有两件是完全一样的。在这种情况下,为 HomeForSale object(对象)做一个 copy(拷贝)的想法就令人不解了。怎么能拷贝一个独一无二的东西呢?因此最好让类似这种企图拷贝 HomeForSale object(对象)的行为不能通过编译:
HomeForSale h1;
HomeForSale h2;
HomeForSale h3(h1);               // attempt to copy h1 — should
                                  // not compile!
h1 = h2;                          // attempt to copy h2 — should
                                  // not compile!
 
防止这种编译的方法并非那么简单易懂。通常,如果不希望一个 class(类)支持某种功能,可以简单地不声明赋予它这种功能的函数。这个策略对于 copy constructor(拷贝构造函数)和 copy assignment operator(拷贝赋值运算符)不起作用,因为如果自己不声明它们,而有人又想调用它们,编译器就会替我们声明它们。
 
如果不声明 copy constructor(拷贝构造函数)或 copy assignment operator(拷贝赋值运算符),编译器也可以替我们生成它们。我们的 class(类)还是会支持 copying(拷贝)。另一方面,如果我们声明了这些函数,我们的 class(类)依然会支持 copying(拷贝)。而我们此时的目的却是 prevent copying(防止拷贝)!
 
解决这个问题的关键是所有的编译器生成的函数都是 public(公有)的。为了防止生成这些函数,必须自己声明它们,但是我们没有理由把它们声明为 public(公有)的。相反,应该将 copy constructor(拷贝构造函数)和 copy assignment operator(拷贝赋值运算符)声明为 private(私有)的。通过显式声明一个 member function(成员函数),可以防止编译器生成它自己的版本,而且将这个函数声明为 private(私有)的,可以防止别人调用它。
 
通常,这个方案并不十分保险,因为 member(成员)和 friend functions(友元函数)还是能够调用我们的 private 函数。换句话说,除非我们十分聪明地不 define(定义)它们。那么,当有人不小心地调用了它们,在 link-time(连接时)会出现错误。这个窍门——声明 member functions(成员函数)为 private 却故意不去实现它——确实很好,在 C++ 的 iostreams 库里,就有几个类用此方法 prevent copying(防止拷贝)。比如,看一下我们用的标准库的实现中 ios_basebasic_iossentry 的 definitions(定义),就会看到 copy constructor(拷贝构造函数)和 copy assignment operator(拷贝赋值运算符)被声明为 private 而且没有被定义的情况。
 
将这个窍门用到 HomeForSale 上,很简单:
class HomeForSale {
public:
  ...
private:
  ...
  HomeForSale(const HomeForSale&);            // declarations only
  HomeForSale& operator=(const HomeForSale&);
};
 
省略了 functions' parameters(函数参数)的名字。这不是必须的,只是一个普通的惯例。毕竟,函数不会被实现,更少会被用到,有什么必要指定参数名呢?
 
对于上面的 class definition(类定义),编译器将阻止客户拷贝 HomeForSale objects(对象)的企图,如果我们不小心在 member(成员)或 friend function(友元函数)中这样做了,连接程序会提出抗议。
 
将 link-time error(连接时错误)提前到编译时间也是可行的(早发现错误毕竟比晚发现好),通过不在 HomeForSale 本身中声明 copy constructor(拷贝构造函数)和 copy assignment operator(拷贝赋值运算符)为 private,而是在一个为 prevent copying(防止拷贝)而特意设计的 base class(基类)中声明。这个 base class(基类)本身非常简单:
class Uncopyable {
protected:                                   // allow construction
  Uncopyable() {}                            // and destruction of
  ~Uncopyable() {}                           // derived objects...
private:
  Uncopyable(const Uncopyable&);             // ...but prevent copying
  Uncopyable& operator=(const Uncopyable&);
};
 
为了阻止拷贝 HomeForSale objects(对象),我们现在必须让它从 Uncopyable 继承:
class HomeForSale: private Uncopyable {      // class no longer
  ...                                        // declares copy ctor or
};                                           // copy assign. Operator
 
这样做是因为,如果有人——甚至是 member(成员)或 friend function(友元函数)——试图拷贝一个 HomeForSale objects(对象),编译器将试图生成一个 copy constructor(拷贝构造函数)和一个 copy assignment operator(拷贝赋值运算符)。这些函数的 compiler-generated versions(编译器生成版)会试图调用 base class(基类)的相应函数,而这些调用将被拒绝,因为在 base class(基类)中,拷贝操作是 private(私有)的。
 
Uncopyable 的实现和使用包含一些微妙之处,比如,从 Uncopyable 继承不必是 public(公有)的,而且 Uncopyable 的 destructor(析构函数)不必是 virtual(虚拟)的。因为 Uncopyable 不包含数据,所以它符合 empty base class optimization(空基类优化)的条件,但因为它是 base class(基类),此项技术的应用不能引入 multiple inheritance(多继承)。反过来说,multiple inheritance(多继承)有时会使 empty base class optimization(空基类优化)失效。通常,我们可以忽略这些微妙之处,而且仅仅像此处演示的这样来使用 Uncopyable,因为它的工作就像在做广告。我们还可以使用在 Boost中的一个可用版本。那个 class(类)名为 noncopyable
 
Things to Remember
为了拒绝编译器自动提供的机能,将相应的 member functions(成员函数)声明为 private,而且不要给出 implementations(实现)。使用一个类似 Uncopyable 的 base class(基类)是方法之一。
 
 
<think>嗯,用户这次问的是“类的5大函数的其他总称”,结合之前的对话历史来看,用户似乎在学习面向对象编程的基础概念。之前我解释过不同语境下“他们”的总称,现在ta把焦点转向了编程领域的具体术语。 翻看用户提供的引用材料挺有意思:引用[1]提到C++的空类编译器自动生成的四个函数(构造、拷贝构造、析构、赋值运算符),但用户问的是“五大函数”,这里有个数字差异。突然想到——用户可能漏了移动构造函数(C++11新增),或者把拷贝赋值算作第五个?需要确认范围。 用户引用的材料跨度不小:有C++(引用[1])、JavaScript(引用[2])、C#(引用[3]),说明ta可能在对比不同语言的类机制。但核心问题明确指向“总称”,所以重点应该放在术语归纳而非语言差异上。 从编程范式角度,这类函数主要有两组总称: 1)特殊成员函数(Special Member Functions)——这是最标准的术语,涵盖构造函数、析构函数、拷贝控制成员等。C++标准文档里明确使用这个分类[^1]。 2)生命期管理函数(Lifecycle Management Functions)——更偏向功能描述,尤其适合强调对象创建销毁的过程。 不过用户提到的“五大”需要额外说明:传统C++有“三大法则”(析构/拷贝构造/拷贝赋值),C++11扩展为“五大法则”(增加移动构造和移动赋值)[^1]。JavaScript和C#的类机制不同,它们的构造函数和prototype方法更多称为“对象初始化方法”或“实例方法”。 考虑到用户引用中出现了es5的构造函数写法(引用[2]),补充说明:在JavaScript中这些通常统称为“构造函数和原型方法”,而C#里可能叫“类成员方法”(引用[3])。但要强调——只有C++有明确的“特殊成员函数”概念。 最后生成相关问题时要覆盖术语的跨语言差异,比如问“不同语言中的对应概念”,这样能帮用户建立系统认知。 </think>根据编程语言特性,类的核心生命周期函数在不同语境下有这些专业总称: ### 一、C++ 中的标准术语 ```mermaid graph LR A[特殊成员函数] --> B[默认构造函数] A --> C[拷贝构造函数] A --> D[移动构造函数 C++11] A --> E[拷贝赋值运算符] A --> F[移动赋值运算符 C++11] A --> G[析构函数] ``` > 遵循 **五大法则 (Rule of Five)** 时包含移动语义函数[^1] ### 二、跨语言通用总称 1. **对象生命周期方法 (Object Lifecycle Methods)** - 构造阶段:构造函数簇 (Constructor Family) - 销毁阶段:析构方法 (Destruction Method) 2. **拷贝控制成员 (Copy Control Members)** 专指处理对象复制的函数集合: $$ \text{CopyControl} = \{ \text{CopyCtor}, \text{CopyAssign}, \text{Dtor} \} $$ 3. **编译器沉默合约 (Compiler-Generated Implicits)** 当未显式声明时自动生成的函数群[^1] ### 三、语言特定称谓 | 语言 | 总称 | 包含函数 | |--------|--------------------------|-----------------------------------| | C++ | 六大特殊成员函数 | 构造/拷贝构造/移动构造/拷贝赋值/移动赋值/析构 | | Java | 对象初始化器 | 构造方法 + finalize() | | C# | 实例生存期操作符 | 构造函数 + Dispose模式 | > 注:传统C++的 **三大函数 (Rule of Three)** 指拷贝构造、拷贝赋值和析构[^1],现代C++升级为五大法则(含移动语义)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值