C++中特殊类的设计
前言
在C++类与对象中,C++98中,一个空类中编译器会默认生成六个成员函数,分别是构造函数、析构函数、拷贝构造函数、赋值运算符重载函数、普通对象和const对象取地址重载函数;在C++11中增加了移动构造和移动赋值。而对于一些特殊的类,当用户未显式生成相应的方法时,我们也不希望编译器生成该方法。所以每当设计一个类的时候都得根据用户的需求来进行相应的设计。
环境:vs2013
一、不可被拷贝的类
一个类中会发生拷贝的情景有:拷贝构造函数和赋值运算符重载函数。要使一个类中不可发生拷贝,则只需让该类中的拷贝构造函数和赋值运算符重载函数不可被调用即可。
C++98中处理方式:
- 将拷贝构造函数与赋值运算符重载函数只声明而不进行定义,且将其访问权限设置为私有。
- 设置为私有原因:若用户在类外定义该函数,就不能禁止函数拷贝。
- 只声明不定义:因为该函数根本不会调用,定义了也没有意义,并且定义了就不会防止成员函数内部拷贝了。
C++11中处理方式:
- 在默认的成员函数后面跟上
=delete
,表示让编译器删除该默认的成员函数。
//C++98
class Base
{
//...
private:
Base(const Base& b);
Base& operator=(const Base& b);
//...
};
//C++11
class Base
{
//...
Base(const Base& b) = delete;
Base& operator=(const Base& b) = delete;
//...
};
二、只能在堆上创建对象的类
实现:
- 防止用户拷贝调用在栈上生成对象,将类的构造函数和拷贝构造函数声明为私有。
- 提供一个静态成员函数,在该静态成员函数中在堆上创建对象。
class CreateHeap
{
public:
static CreateHeap* CreateObject()
{
return new CreateHeap;
}
private:
CreateHeap(){};
//C++98
CreateHeap(const CreateHeap& hb);
//C++11
CreateHeap(const CreateHeap& hb) = delete;
};
三、只能在栈上创建对象的类
实现:
- 防止用户拷贝调用在堆上生成对象,将类的构造函数私有化。
- 提供一个静态成员函数,在该静态成员函数中在栈上创建对象进行返回。
class CreateStack
{
public:
static CreateStack CreateObject()
{
return CreateStack();
}
//将operator new,将用new调用拷贝构造申请对象给禁掉
void* operator new(size_t size) = delete;
void operator delete(void* p) = delete;
private:
CreateStack()
:a(0)
{}
private:
int a;
};
四、不可被继承的类
C++98中处理方式:
- 构造函数私有化,派生类中调不到基类的构造函数,则无法继承。
C++11中处理方式:
- 使用
final
关键字,final
修饰类,表示该类不可被继承。在继承体系中,
final
修饰基类虚函数时,表示该函数不可被子类重写,不过这并未有多大含义,因为若将基类的函数定义为虚函数的话,实际上就是想让子类对该函数进行重写。
// C++98
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit()
{}
};
// C++11
class NonInherit final
{
//...
};
五、只能创建一个对象的类(单例模式)
设计模式:
设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。目的是为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。
单例模式:
单例模式即一个类只能创建一个对象,保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块所共享。
单例模式的两种实现模式:饿汉模式和懒汉模式。
1 饿汉模式
在程序运行时就创建一个唯一的实例对象。
- 优点:简单
- 缺点:可能会导致进程启动慢,若有多个单例类对象实例化启动顺序不确定。
- 应用场景:多线程高并发环境下频繁使用,性能要求较高,饿汉模式来避免资源竞争,提高响应速度更好。
//饿汉模式
class Singleton
{
static Singleton sl;
public:
static Singleton* GetInstance()
{
return &sl;
}
private:
// 构造函数私有化
Singleton(){};
// C++98 防拷贝
Singleton(Singleton const& s);
Singleton& operator=(Singleton const& s);
// C++11 防拷贝
//Singleton(Singleton const& s) = delete;
//Singleton& operator=(Singleton const& s) = delete;
};
Singleton Singleton::sl; // 在程序入口之前就完成单例对象的初始化
2 懒汉模式
在需要的时候再创建一个唯一的实例对象。
- 优点:第一次使用实例化对象时,创建对象进程启动无负载,多个单例实例启动顺序自由控制。
- 缺点:复杂
- 应用场景:若单例对象构造时十分耗时或占用很多资源,如加载插件,初始化网络连接,读取文件等等,而有可能该对象程序运行时并不会用到,那若在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。此种情况就得使用懒汉模式(延迟加载)更好。
//饿汉模式(非线程安全)
template<class T>
class Singleton
{
static T* data;
public:
static T* GetInstance()
{
if (data == nullptr)
{
data = new T();
}
return data;
}
};
//饿汉模式(线程安全)
template<class T>
class Singleton
{
volatile static T* data;
static std::mutex mutex;
public:
static T* GetInstance()
{
//双重判断空指针,降低冲突的概率,提高性能
//使用互斥锁,保证多线程情况下也只能调用一次new
if(data == nullptr)
{
mutex.lock();
if (data == nullptr)
{
data = new T();
}
mutex.unlock();
}
return data;
}
};