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++ 开发(推荐)
相关推荐
倒头就睡的小比特14 小时前
算法竞赛C++常用的STL
c++·算法
weilx123414 小时前
C++笔记-文件IO-<fcntl.h>
c++
小羊没烦恼!15 小时前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
伞伞悦读16 小时前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
Smileyqp沛沛16 小时前
前端?C++ ?较大差异基础罗列
c++·基础·前端转c++
C语言小火车16 小时前
C/C++ 为什么需要编译器?
开发语言·c++
旖旎夜光17 小时前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
吞下星星的少年·-·17 小时前
C++ 萌新语法入门篇
c++·算法比赛
霍霍的袁17 小时前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
another heaven17 小时前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法