一、设计一个不能被拷贝的类
C++中类的拷贝只会出现在拷贝构造和赋值运算符重载上,因此,我们只需要设法让这两个函数无法被调用就可以了。
在C++98中,我们可以通过将类的拷贝构造和复制运算符重载写在private中,这样我们在类外就无法调用了。
class CopyBan
{// ...private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);//...
}
我们可以通过只声明不实现的方式来防止类内部的拷贝。
到了C++11,C++拓展了的关键字delete的功能,delete除了释放new申请的资源外,如果在默认成员函数后跟上 =delete,表示让编译器删除掉该默认成员函数。(delete可以禁止默认成员函数的生成,或者是禁止任何一个成员的调用)。
class CopyBan
{// ...CopyBan(const CopyBan&)=delete;CopyBan& operator=(const CopyBan&)=delete;//...
};
二、设计一个只能在堆上创建对象的类
首先,我们来了解一下堆与栈。
内存中的栈区由系统自动管理,用于存储函数调用时的局部变量、参数及返回地址,其内存分配和释放通过移动栈指针快速完成,生命周期与函数绑定,超出固定大小会导致栈溢出;而堆区则由程序员手动申请和释放(如通过 malloc
/free/new/delete
),适合存放动态分配或生命周期不确定的数据(如大型对象),虽然容量更大且灵活,但分配速度较慢,且管理不当易引发内存泄漏或碎片化问题。栈通常从高地址向低地址增长,每个线程独立拥有栈,而堆从低地址向高地址扩展,由进程内所有线程共享。
- 栈区:局部变量、函数参数、返回地址等,由编译器自动管理。
- 堆区:动态分配的内存,手动管理,可能涉及new/delete或malloc/free。
- 静态存储区:全局变量、静态变量(包括类静态成员),分为初始化和未初始化部分。
- 常量区:字符串常量、const变量等,可能放在只读区域。
- 代码区:函数代码、虚函数表、函数指针等。
实现方法:
class HeapOnly
{
public: static HeapOnly* CreateObject() { return new HeapOnly; }
private: HeapOnly() {}// C++98// 1.只声明,不实现。因为实现可能会很麻烦,而你本身不需要// 2.声明成私有HeapOnly(const HeapOnly&);// or // C++11 HeapOnly(const HeapOnly&) = delete;
};
三、设计一个只能在栈上创建对象的类
class StackOnly
{
public:static StackOnly CreateObj(){return StackOnly();}// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉// StackOnly obj = StackOnly::CreateObj();// StackOnly* ptr3 = new StackOnly(obj);void* operator new(size_t size) = delete;void operator delete(void* p) = delete;
private:StackOnly():_a(0){}
private:int _a;
};
四、设计一个不能被继承的类
C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承。
class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit(){}
};
C++11使用final修饰类,表示类无法被继承。
class A final
{// ....
};
五、设计一个只能创建一个对象的类
设计模式:是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
单例模式:一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
单例模式有两种实现:
1.饿汉模式:
不管将来用不用,程序启动时就创建一个唯一的实例对象。
// 饿汉模式
// 优点:简单
// 缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。
class Singleton
{
public:static Singleton* GetInstance(){return &m_instance;}
private:// 构造函数私有Singleton() {};// C++98 防拷贝Singleton(Singleton const&);Singleton& operator=(Singleton const&);// or// C++11Singleton(Singleton const&) = delete;Singleton& operator=(Singleton const&) = delete;static Singleton m_instance;
};Singleton Singleton::m_instance;//在程序入口之前就完成单例对象的初始化
2.懒汉模式
在首次请求时创建实例(延迟加载)。解决了饿汉模式的资源浪费问题,但是带来了饿汉模式不存在的线程安全问题。
class Singleton {
public:static Singleton& GetInstance() {static Singleton instance; // 首次调用时初始化return instance;}
private:Singleton() = default; // 私有构造函数
};
考虑到线程安全问题
class Singleton
{
public:static Singleton* GetInstance() {// 注意这里一定要使用Double-Check的方式加锁,才能保证效率和线程安全if (nullptr == m_pInstance) {m_mtx.lock();if (nullptr == m_pInstance) {m_pInstance = new Singleton();}m_mtx.unlock();}return m_pInstance;}// 实现一个内嵌垃圾回收类 class CGarbo {public:~CGarbo() {if (Singleton::m_pInstance)delete Singleton::m_pInstance;}};// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象static CGarbo Garbo;
private:// 构造函数私有Singleton() {};// 防拷贝Singleton(Singleton const&);Singleton& operator=(Singleton const&);static Singleton* m_pInstance; // 单例对象指针static mutex m_mtx; //互斥锁
};
Singleton* Singleton::m_pInstance = nullptr;
Singleton::CGarbo Garbo;
mutex Singleton::m_mtx;int main()
{thread t1([] {cout << &Singleton::GetInstance() << endl; });thread t2([] {cout << &Singleton::GetInstance() << endl; });t1.join();t2.join();cout << &Singleton::GetInstance() << endl;cout << &Singleton::GetInstance() << endl;return 0;
}