C++设计一个不能被拷贝的特殊类

在 C++ 编程中,我们有时需要设计禁止拷贝的类 。

比如单例类、管理唯一资源(如文件句柄、网络连接)的类。

拷贝这类对象会导致资源重复释放、逻辑错误等问题。

本文将详细讲解 C++98 和 C++11 两种标准下,实现 "禁止类拷贝" 的方法、原理及最佳实践。

一、为什么要禁止拷贝?

拷贝操作在 C++ 中主要发生在两个场景:

  1. 拷贝构造函数 :用已有对象创建新对象时调用(如 CopyBan obj2(obj1););
  2. 赋值运算符重载 :用一个对象给另一个已存在的对象赋值时调用(如 obj2 = obj1;)。

想要禁止类的拷贝,核心就是让这两个函数无法被调用------ 无论是类外部代码,还是类内部成员函数,都无法触发拷贝操作。

二、C++98 实现方案:私有化 + 只声明不定义

C++98 没有专门的 "禁用函数" 语法,只能通过 "访问权限 + 函数声明策略" 实现禁止拷贝。

1. 代码实现

cpp 复制代码
class CopyBan 
{
public:
    // 普通构造函数(示例)
    CopyBan(int data = 0) : _data(data) {}

    // 其他成员函数(示例)
    void Print() const 
    {
        std::cout << "Data: " << _data << std::endl;
    }

private:
    // 关键:只声明、不定义,且设为私有
    CopyBan(const CopyBan&);                  // 拷贝构造函数
    CopyBan& operator=(const CopyBan&);       // 赋值运算符重载

    // 类成员变量(示例)
    int _data;
};

2. 核心原理

(1)设为私有:防止外部定义 / 调用

如果仅声明但不设置为 private,用户可能在类外自行定义这两个函数,导致 "禁止拷贝" 的目的失效。

  • 外部代码调用拷贝构造 / 赋值运算符时,会因 "访问私有成员" 直接编译报错;
  • 即使是类的友元函数,也无法调用(因为函数只有声明、没有定义,链接阶段会报错)。
(2)只声明不定义:防止内部拷贝

不定义这两个函数的原因:

  • 这两个函数本就不应该被调用,定义无任何实际意义;
  • 如果定义了,类的成员函数 / 友元函数可能在内部触发拷贝(比如 void func() { CopyBan tmp(*this); }),违背 "禁止拷贝" 的初衷;
  • 仅声明不定义时,若内部误调用,链接阶段会报 "未定义的引用" 错误,及时暴露问题。

3. 测试验证

cpp 复制代码
int main() 
{
    CopyBan obj1(10);
    CopyBan obj2(obj1);    // 编译报错:拷贝构造函数是私有成员
    CopyBan obj3;
    obj3 = obj1;           // 编译报错:赋值运算符是私有成员
    return 0;
}

三、C++11 实现方案:=delete 显式删除

C++11 扩展了**delete** 的用法:在默认成员函数后加 =delete ,表示让编译器删除该函数,直接禁止其调用(比 C++98 更简洁、语义更清晰)。

1. 代码实现

cpp 复制代码
class CopyBan 
{
public:
    // 普通构造函数(示例)
    CopyBan(int data = 0) : _data(data) {}

    // 关键:显式删除拷贝构造和赋值运算符
    CopyBan(const CopyBan&) = delete;                  // 删除拷贝构造函数
    CopyBan& operator=(const CopyBan&) = delete;       // 删除赋值运算符重载

    // 其他成员函数(示例)
    void Print() const 
    {
        std::cout << "Data: " << _data << std::endl;
    }

private:
    // 类成员变量(示例)
    int _data;
};

2. 核心原理

  • =delete编译期限制:编译器会直接拒绝生成 / 调用这两个函数,无论类内、类外调用,都会在编译阶段报错(比 C++98 的 "链接期报错" 更及时);
  • 无需设置访问权限(即使写在 public 里,也无法调用),语义更直观 ------"明确告诉编译器:我要删除这个函数"。

3. 测试验证

cpp 复制代码
int main() 
{
    CopyBan obj1(10);
    CopyBan obj2(obj1);    // 编译报错:使用了被删除的函数
    CopyBan obj3;
    obj3 = obj1;           // 编译报错:使用了被删除的函数
    return 0;
}

四、两种方案对比

特性 C++98 方案 C++11 方案
实现方式 私有化 + 只声明不定义 显式 =delete 删除函数
报错阶段 编译(外部调用)/ 链接(内部调用) 编译期直接报错
语义清晰度 间接实现,需理解 "私有 + 未定义" 逻辑 直接、直观,语义明确
适用场景 旧编译器、C++98 环境 现代 C++ 开发(推荐)
相关推荐
似水明俊德2 小时前
10-C#
开发语言·windows·c#
似水明俊德2 小时前
14-C#
开发语言·c#
勇敢牛牛_2 小时前
【aiway】基于 Rust 开发的 API + AI 网关
开发语言·后端·网关·ai·rust
khddvbe2 小时前
C++中的代理模式实战
开发语言·c++·算法
计算机安禾2 小时前
【C语言程序设计】第31篇:指针与函数
c语言·开发语言·数据结构·c++·算法·leetcode·visual studio
kaikaile19952 小时前
庞加莱截面计算MATLAB程序
开发语言·matlab
酬勤-人间道2 小时前
自研软件模型处理全流程|个人开发经验分享
c++·经验分享·计算机·计算机图形学·桩号·开挖·回填
郝学胜-神的一滴2 小时前
算法奇旅:探寻3/5/7素因子之第k特殊数——优雅的多路指针解法全解析
数据结构·c++·算法·职场和发展