转向现代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` 只能用于成员函数。

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

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)
报错时机 外部:编译期;内部/友元:链接期 任何地方:编译期
适用范围 仅限类成员函数 任何函数(含非成员函数、模板特化)
代码可读性 意图隐晦(依赖不写实现的技巧) 意图极其明确(代码自解释)
相关推荐
cfm_29148 小时前
Spring AI Tool 调用架构全解
java·人工智能·spring
法哥20129 小时前
用 C++ 控制 CMW100,到底在干什么?
开发语言·c++
sun༒9 小时前
Spring @Scheduled 定时任务详解:Cron表达式、执行顺序、优先级与并行调度
java·后端·spring
一次旅行9 小时前
DeepSeek‑V4‑Flash‑Vision‑Exp 小白入门实战|3种传图方式、完整可跑代码、避坑排障
java·前端·人工智能
野生技术架构师9 小时前
Spring Boot 实现数据脱敏:自定义注解 + Jackson 序列化器
java·spring boot·后端
AI人工智能+电脑小能手9 小时前
大白话说Java设计模式-28-模板方法模式(业务实战篇)
java·设计模式·模板方法模式·spring源码·订单系统·代码复用
奥莱维9 小时前
KNX酒店方案_KNX专用线与高端酒店技术逻辑
java·服务器·前端·数据库
_Narcissus_9 小时前
枚举和模拟算法笔记
c语言·数据结构·c++·笔记·算法·模拟·枚举
工业一体机老司机9 小时前
Linux工业一体机自启动服务配置:systemd服务单元编写与开机优化实战
java·linux·服务器
冻柠檬飞冰走茶10 小时前
《数据结构实验指导-C++语言版》 在顺序表 list 中查找元素 x
开发语言·数据结构·c++·算法·list