转向现代C++——优先选用删除函数而非private未定义函数

文章目录

    • [优先选用删除函数,而非 private 未定义函数](#优先选用删除函数,而非 private 未定义函数)
      • [Private 未定义](#Private 未定义)
      • [Delete 函数](#Delete 函数)

优先选用删除函数,而非 private 未定义函数

Private 未定义

在 C++98 中,如果想阻止用户拷贝某个类的对象(例如单例模式或不可复制的资源管理器),通常会将拷贝构造函数和拷贝赋值运算符声明为 private,并且不提供它们的实现。

cpp 复制代码
class WidgetC98 {
private:
    // 1. 声明为 private 阻止了外部直接调用
    WidgetC98(const WidgetC98&);            
    WidgetC98& operator=(const WidgetC98&);

public:
    WidgetC98() {}
    void doSomething() {}
};

// 外部调用测试
void testC98() {
    WidgetC98 w1;
    // WidgetC98 w2(w1); // 编译期错误!错误信息通常是:'WidgetC98::WidgetC98' is private within this context
}

:::color4

局限性

  • 成员函数和友元仍能调用****: 如果该类内部的成员函数或者它的 friend 友元类不小心调用了拷贝构造,编译期可以通过。
  • 链接期报错延迟****: 只有到了链接阶段(Link time) ,链接器发现找不到该函数的定义,才会报错(通常会报诸如 undefined reference to... 的错误)。链接期错误通常比编译期错误更难排查。

:::

Delete 函数

C++11 引入了 = delete 关键字,直接在编译期禁用了特定的函数。

cpp 复制代码
class WidgetC11 {
public:
    WidgetC11() {}

    // 1. 优先使用 = delete,且习惯上放至 public 区域
    WidgetC11(const WidgetC11&) = delete;            
    WidgetC11& operator=(const WidgetC11&) = delete;

    void doSomething() {
        // 即使在成员函数内部尝试拷贝,编译期也会立刻报错!
        // WidgetC11 wInner(*this); 
    }
};

void testC11() {
    WidgetC11 w1;
    // WidgetC11 w2(w1); // 编译期错误!错误信息极其明确:use of deleted function 'WidgetC11::WidgetC11(const WidgetC11&)'
}

:::info

优势:

  • 更早的错误捕捉 : 无论是外部调用,还是类内部/友元调用,全部在编译期(Compile time)直接报错,绝不留到链接期。
  • 更好的错误提示****: 编译器会明确指出该函数已被"删除(deleted)",而不是晦涩的"无法访问隐式函数"或"未定义的引用"。
  • 习惯放在 public**: ** 建议将 = delete 函数设为 public。因为当外部调用时,编译器会先检查访问权限(private),再检查删除状态。如果放 private,某些编译器只会报"无权访问"的错误,隐藏了该函数"已被禁用"的真正意图。

:::

🔊删除函数= delete 的威力不仅限于禁用拷贝构造,它可以应用在任何函数上,而 C++98 的 private` 只能用于成员函数。

假设有一个处理整数的普通函数,你想防止用户传入 double 或 char 导致隐式转换:

cpp 复制代码
// 只接受真正的 int
void processInt(int value);

// 使用 = delete 禁用 float 和 double 的重载
void processInt(double) = delete;
void processInt(float) = delete;

void testConversion() {
    processInt(42);       // 正确:调用 processInt(int)
    // processInt(3.14);  // 编译期错误!尝试调用已被删除的 processInt(double)
}

如果写了一个模板函数,但想排除某些特定的类型,C++98 很难优雅地实现,而 C++11 配合 = delete 极其简单:

cpp 复制代码
// 通用模板:允许大部分指针
template<typename T>
void processPointer(T* ptr) {
    // ...
}

// 特化:明确禁止 void* 指针和 char* 指针
template<> void processPointer<void>(void*) = delete;
template<> void processPointer<char>(char*) = delete;

void testTemplate() {
    int x = 10;
    processPointer(&x);     // 正确

    void* pVoid = &x;
    // processPointer(pVoid); // 编译期错误!使用了删除的特化版本
}

在 C++98 中,如果想对模板做到这一点,只能把特化版本写进一个私有类模板里,代码会变得极其臃肿和晦涩。

特征 C++98 (private 未定义) C++11 (= delete)
报错时机 外部:编译期;内部/友元:链接期 任何地方:编译期
适用范围 仅限类成员函数 任何函数(含非成员函数、模板特化)
代码可读性 意图隐晦(依赖不写实现的技巧) 意图极其明确(代码自解释)
相关推荐
Cc.Y1 小时前
Java零基础入门:字符串深度掌握——从基础API到StringBuilder性能优化
java·开发语言·性能优化
左手の明天6 小时前
SLIP 协议封装与解析详解:原理、C 代码与实战案例
linux·c语言·开发语言·c++
shuoshuohaohao6 小时前
MyBatis 小知识点
java·spring·mybatis
海上小飞龙6 小时前
【KMP算法-下篇】同一道题:Java 库函数 2600 微秒,手写 KMP 16 微秒
java·开发语言·算法
青少儿编程课堂6 小时前
后缀自动机解析:本质不同子串与最长重复片段统计
c++·python·算法·bfs·信息学竞赛
朝朝辞暮i6 小时前
C++ 第 12 课:局部变量、作用域、变量生命周期
开发语言·c++·算法
谢亮_vipxieliang7 小时前
泛型擦除、通配符与 PECS 一次讲透
java·面试
八解毒剂7 小时前
【计组】中央处理器CPU
java·开发语言·计算机组成原理
打工仔折腾 AI8 小时前
工业场景下时序库与实时计算一体化架构选型实践对比
java·开发语言·后端·python·性能优化·架构·ai agent 实战
ZealSinger8 小时前
Java虚拟线程上线后瓶颈转移到哪看三层
java·开发语言·firefox