C++知识点 -- 特殊类设计
C++知识点 – 特殊类设计
文章目录
- C++知识点 -- 特殊类设计
- 一、不能被拷贝的类
- 二、 只能在堆上创建对象的类
- 三、 只能在栈上创建对象的类
- 四、 不能被继承的类
- 五、 只能创建一个对象的类(单例模式)
- 1.饿汉模式
- 2.懒汉模式
- 3. 单例对象释放问题:
一、不能被拷贝的类
不让编译器默认生成拷贝构造和赋值重载,c++11可以在默认成员函数后加上 = delete,表示让编译器删除该默认成员函数;
class CopyBan
{
public:
CopyBan(const CopyBan& cb) = delete;
CopyBan& operator=(const CopyBan& cb) = delete;
private:
int _a;
};
二、 只能在堆上创建对象的类
对于普通类,在栈、堆和静态区都能够创建对象;
要设计只能在堆上创建对象的类,就需要堆构造函数或者析构函数做出一定调整;
方法一
将析构函数设为私有,这样在栈和静态区的对象就无法调用析构函数销毁对象,只有在对上创建的对象调用delete才能够销毁对象,可以写一个公有函数来delete对象,完成对象的销毁;
class HeapOnly
{
public:
//公有函数调用delete销毁对象,设置成静态成员,使用类域就能够调用
static void Delete(HeapOnly* p)
{
delete p;
}
private:
//析构函数私有化
~HeapOnly()
{}
int _a;
};
可以看出,栈和静态区都无法创建StackOnly对象,只有通过new才能在堆上创建对象;
方法二
将构造函数私有化,这样在哪里都无法创建对象了,再添加一个公有的静态成员函数,完成函数内部在堆上创建对象,同时将拷贝构造和赋值重载也设为私有,否则还能通过拷贝的方式在其他地方创建对象;
class HeapOnly
{
public:
//提供一个公有的,在堆上创建对象的方式
static HeapOnly* CreatObj()
{
HeapOnly* pho = new HeapOnly;
return pho;
}
private:
//构造函数私有化
HeapOnly()
{}
//拷贝构造和赋值重载也删除
HeapOnly(const HeapOnly& so) = delete;
HeapOnly& operator=(const HeapOnly& so) = delete;
int _a;
};
效果一样;
三、 只能在栈上创建对象的类
将构造函数私有化,提供一个公有的静态成员函数,函数内部调用构造函数在堆上创建对象,返回该对象;
但是这种方式并不能阻止拷贝构造在其他地方创建对象,简单地将拷贝构造和赋值重载delete,会导致CreateObj这个函数传值返回出问题,因为传值返回需要调用拷贝构造,我们可以通过delete掉operator new来限制new创建对象;
class StackOnly
{
public:
static StackOnly CreateObj()
{
StackOnly so;
return so;
}
private:
StackOnly()
: _a(0)
{}
void* operator new(size_t n) = delete;
int _a;
};
这种方法并不能防止在静态区创建对象;
四、 不能被继承的类
在c++11中,用final修饰一个类,表示该类不能被继承;
//该类无法被继承
class A final
{
};
五、 只能创建一个对象的类(单例模式)
一个类只能创建一个对象,就是单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享;
单例模式有两种实现:
1.饿汉模式
程序启动时就创建一个唯一实例对象;
(1)构造函数私有化,就不能通过其他方式初始化了;
(2)私有成员中声明一个静态的指向该对象的指针,静态成员属于这个类;
(3)在类外使用new初始化这个静态指针;
(4)提供公有函数来返回这个指向对象的静态指针;
class Singleton
{
public:
static Singleton* GetInstace()
{
return _pinst;
}
void* Alloc(size_t n)
{
void* ptr = nullptr;
//...
return ptr;
}
void Delloc(void* ptr)
{
//...
}
private:
//构造函数私有化
Singleton()
{}
char* _ptr = nullptr;
static Singleton* _pinst;//声明
};
//定义
Singleton* Singleton::_pinst = new Singleton;
//使用
void test()
{
void* ptr1 = Singleton::GetInstace()->Alloc(10);
Singleton::GetInstace()->Delloc(ptr1);
}
优点:简单、没有线程安全问题
缺点:
(1)一个程序中,多个单例,并且有先后创建初始化顺序要求时,饿汉无法控制。
比如程序两个单例类A 和 B,假设要求A先创建初始化,B再创建初始化。
(2)饿汉单例类,初始化时任务多,会影响程序启动速度。
2.懒汉模式
第一次使用对象时才创建实例对象;
(1)构造函数私有化,私有静态对象指针;
(2)公有静态成员函数,只有在对象指针为空的时候才创建对象,返回指向对象的指针;
(3)需要释放资源的场景,可以内嵌一个垃圾回收类;
实例化出一个静态垃圾回收对象,出了对象作用域(main函数结束后)就会调用析构,在析构里面将单例对象的资源全部释放;
class Singleton
{
public:
static Singleton* GetInstance()
{
if (_pinst == nullptr)
{
_pinst = new Singleton;
}
return _pinst;
}
void* Alloc(size_t n)
{
void* ptr = nullptr;
//...
return ptr;
}
void Delloc(void* ptr)
{
//...
}
//内嵌垃圾回收类
class CGarbo
{
public:
~CGarbo()
{
if (_pinst)
{
delete _pinst;
}
}
};
private:
//构造函数私有化
Singleton()
{}
char* _ptr = nullptr;
static Singleton* _pinst;//声明
};
//定义
Singleton* Singleton::_pinst = nullptr;
//回收对象:main函数结束后,会调用析构函数,就会释放单例对象
static Singleton::CGarbo gc;
void test()
{
void* ptr1 = Singleton::GetInstance()->Alloc(10);
Singleton::GetInstance()->Delloc(ptr1);
}
优点:
(1)控制顺序
(2)不影响启动速度
缺点:
(1)相对复杂
(2)线程安全问题要处理好
3. 单例对象释放问题:
(1)一般情况下,单例对象不需要释放的。因为一般整个程序运行期间都可能会用它。
单例对象在进程正常结束后,也会资源释放。
(2)有些特殊场景需要释放,比如单例对象析构时,要进行一些持久化(往文件、数据库写)操作。