目录
[一、精准控制对象创建:仅堆 / 仅栈实例化](#一、精准控制对象创建:仅堆 / 仅栈实例化)
[1. 仅允许在堆上创建对象(HeapOnly)](#1. 仅允许在堆上创建对象(HeapOnly))
[2. 仅允许在栈上创建对象(StackOnly)](#2. 仅允许在栈上创建对象(StackOnly))
[1. 禁止拷贝构造与赋值(CopyBan)](#1. 禁止拷贝构造与赋值(CopyBan))
[2. 不可被继承的类(NonInherit)](#2. 不可被继承的类(NonInherit))
[三、设计模式实战:单例模式(饿汉 / 懒汉)](#三、设计模式实战:单例模式(饿汉 / 懒汉))
[1. 饿汉模式:饿汉式单例](#1. 饿汉模式:饿汉式单例)
[2. 懒汉模式:懒汉式单例](#2. 懒汉模式:懒汉式单例)
在 C++ 面向对象编程中,灵活控制类对象的创建方式、限制拷贝行为、实现单例模式等,是提升代码健壮性和设计合理性的核心技巧。本文将从 "仅堆 / 仅栈创建对象""禁止拷贝""不可继承""单例模式(饿汉 / 懒汉)" 等场景出发,详解各类特殊类的设计思路与实现细节,帮助你掌握 C++ 类设计的进阶玩法。
一、精准控制对象创建:仅堆 / 仅栈实例化
在实际开发中,我们有时需要限制对象的创建位置(比如仅允许堆上创建,避免栈溢出;或仅允许栈上创建,管控内存),核心思路是通过构造函数私有化 + 运算符禁用 实现。
1. 仅允许在堆上创建对象(HeapOnly)
要禁止栈上创建对象,关键是私有化构造函数(栈对象创建依赖公开的构造函数),同时提供静态成员函数作为 "对象创建入口",并禁用拷贝构造 / 赋值(防止通过拷贝绕开限制)。
cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class HeapOnly
{
public:
// 静态成员函数:唯一的对象创建入口,返回堆上对象的指针
static HeapOnly* CreateObj(int a)
{
HeapOnly* obj = new HeapOnly; // 堆上创建对象
obj->_a = a;
return obj;
}
int get() { return _a; } // 辅助函数:获取成员变量
private:
// 1. 构造函数私有化:禁止栈上直接创建(如 HeapOnly obj;)
HeapOnly() : _a(0) {}
// 2. 禁用拷贝构造(C++11):避免通过拷贝创建栈对象
HeapOnly(const HeapOnly&) = delete;
// 3. 禁用赋值运算符:避免对象拷贝赋值
HeapOnly& operator=(const HeapOnly&) = delete;
int _a; // 成员变量
};
int main()
{
// 正确:通过静态函数获取堆对象
HeapOnly* sp1 = HeapOnly::CreateObj(123);
cout << sp1->get() << endl; // 输出 123
// 错误:栈上创建被禁止(构造函数私有)
// HeapOnly obj;
// 错误:拷贝构造被禁用
// HeapOnly* sp2 = new HeapOnly(*sp1);
delete sp1; // 注意释放堆对象
return 0;
}
核心原理:栈对象的创建会直接调用构造函数(私有化后无法调用),而静态成员函数属于类而非对象,可访问私有构造函数,从而唯一掌控对象的堆创建逻辑。
2. 仅允许在栈上创建对象(StackOnly)
要禁止堆上创建对象,需禁用 new/delete 运算符 (堆对象依赖 new 分配内存),同时私有化构造函数,通过静态函数返回栈对象(利用返回值优化,避免拷贝)。
cpp
class StackOnly
{
public:
// 静态函数:返回栈对象(编译器会优化拷贝,无性能损耗)
static StackOnly CreateObj(int a = 0)
{
return StackOnly(a);
}
void PrintA() const { cout << "_a = " << _a << endl; }
// 禁用 new/delete 及其数组版本:彻底禁止堆创建
void* operator new(size_t size) = delete;
void operator delete(void* ptr) = delete;
void* operator new[](size_t size) = delete;
void operator delete[](void* ptr) = delete;
private:
// 构造函数私有化:避免直接创建(必须通过 CreateObj)
StackOnly(int a = 0) : _a(a) {}
// 禁用所有拷贝/移动语义:防止绕开限制
StackOnly(const StackOnly&) = delete;
StackOnly& operator=(const StackOnly&) = delete;
StackOnly(StackOnly&&) = delete;
StackOnly& operator=(StackOnly&&) = delete;
int _a;
};
int main()
{
// 正确:栈上创建对象
StackOnly obj1 = StackOnly::CreateObj(10);
obj1.PrintA(); // 输出 _a = 10
// 错误:堆创建被禁用(new 运算符已删除)
// StackOnly* ptr = new StackOnly::CreateObj(20);
return 0;
}
核心原理 :new 运算符底层会调用 operator new,禁用该运算符后,无法通过 new 分配堆内存;静态函数返回栈对象时,编译器的 "返回值优化(RVO)" 会直接在调用方栈上构造对象,无需拷贝。
二、限制类的行为:禁止拷贝、不可继承
1. 禁止拷贝构造与赋值(CopyBan)
某些场景下(如单例、资源独占类),需要禁止对象拷贝,核心是私有化并删除拷贝构造和赋值运算符 (C++11 后推荐用 = delete)。
cpp
class CopyBan
{
public:
CopyBan() {} // 构造函数公开,允许创建对象
private:
// 禁用拷贝构造和赋值运算符
CopyBan(const CopyBan&) = delete;
CopyBan& operator=(const CopyBan&) = delete;
};
int main()
{
CopyBan obj1;
// 错误:拷贝构造被禁用
// CopyBan obj2 = obj1;
// 错误:赋值运算符被禁用
// CopyBan obj3;
// obj3 = obj1;
return 0;
}
2. 不可被继承的类(NonInherit)
C++ 中没有原生的 final 关键字(C++11 后支持),传统方式是将构造函数私有化------ 子类构造时需调用父类构造函数,私有化后子类无法访问,从而禁止继承。
cpp
class NonInherit
{
public:
// 静态函数:获取类对象(唯一创建入口)
static NonInherit CreateObj()
{
return NonInherit();
}
private:
// 构造函数私有化:子类无法调用,禁止继承
NonInherit() {}
};
// 错误:子类无法访问父类私有构造函数
// class Derived : public NonInherit {};
int main()
{
NonInherit obj = NonInherit::CreateObj();
return 0;
}
补充 :C++11 后可直接用 final 关键字(更简洁):
cpp
class NonInherit final {};
三、设计模式实战:单例模式(饿汉 / 懒汉)
单例模式确保一个类只有一个实例,并提供全局访问点。根据实例初始化时机,分为 "饿汉模式"(程序启动时初始化)和 "懒汉模式"(首次使用时初始化)。
1. 饿汉模式:饿汉式单例
"饿汉" 即 "迫不及待",在程序入口前完成单例对象初始化,优点是线程安全(无需加锁),缺点是可能提前占用资源。
cpp
class Singleton
{
public:
// 全局访问点:返回单例对象的引用(避免拷贝)
static Singleton& GetInstance()
{
return *_inst;
}
private:
// 1. 构造函数私有化 + 禁用拷贝:防止外部创建/拷贝
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 2. 静态指针:存储单例对象(程序启动时初始化)
static Singleton* _inst;
};
// 全局静态成员初始化:程序入口前完成,线程安全
Singleton* Singleton::_inst = new Singleton;
int main()
{
// 获取单例对象
Singleton& obj1 = Singleton::GetInstance();
Singleton& obj2 = Singleton::GetInstance();
// 验证:obj1 和 obj2 是同一个对象
cout << &obj1 << " " << &obj2 << endl; // 地址相同
return 0;
}
2. 懒汉模式:懒汉式单例
"懒汉" 即 "懒加载",首次调用 GetInstance 时才初始化单例对象,优点是按需分配资源,缺点是多线程下需加锁保证线程安全(双检查锁优化)。
cpp
#include <mutex> // 引入互斥锁
class Singleton
{
public:
// 全局访问点:双检查锁 + 懒加载
static Singleton& GetInstance()
{
// 第一层检查:避免每次调用都加锁(提升性能)
if (_inst == nullptr)
{
_mtx.lock(); // 加锁:保证线程安全
// 第二层检查:防止多个线程同时通过第一层检查
if (_inst == nullptr)
{
_inst = new Singleton;
}
_mtx.unlock(); // 解锁
}
return *_inst;
}
private:
// 构造函数私有化 + 禁用拷贝
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 静态成员:单例指针 + 互斥锁
static Singleton* _inst;
static mutex _mtx;
};
// 静态成员初始化:程序启动时置空
Singleton* Singleton::_inst = nullptr;
mutex Singleton::_mtx; // 初始化互斥锁
int main()
{
Singleton& obj = Singleton::GetInstance();
return 0;
}
双检查锁原理:
- 第一层
if:如果单例已创建,直接返回,避免每次加锁的性能损耗; - 加锁后第二层
if:防止多个线程同时通过第一层检查,重复创建对象。
四、总结
本文讲解的各类特殊类设计,核心都是围绕构造函数、运算符、静态成员的灵活运用:
- 控制对象创建位置:私有化构造函数 + 禁用
new/delete/ 拷贝; - 限制类行为:删除拷贝构造 / 赋值、私有化构造函数;
- 单例模式:私有化构造 + 静态指针 + 全局访问点(饿汉提前初始化,懒汉双检查锁)。
这些技巧在框架开发、资源管控、设计模式落地等场景中广泛应用,掌握后能大幅提升 C++ 代码的设计能力和健壮性。