目录
前言
一,请设计一个不能被拷贝的类
二,请设计一个只能在堆上创建对象的类
2.1 思路一:构造函数私有
2.2 思路二,析构函数私有
三,请设计一个只能在栈上创建对象的类
四,请设计一个只能创建一个对象的类(单例模式)
4.1 关于设计模式
4.2 单例模式
4.3 饿汉模式
4.4 懒汉模式
前言
C++的类是C++这门面向对象语言的精华所在,C++的类设计在众多OO语言中也是非常具有代表性的存在。所以,原本的类功能已经可以满足我们大部分的需求,但是随着编程语言的不断发展和实际应用的持续复杂,类原本的功能可能会有一部分失效或缺陷。
所以就有了特殊类设计,通过设计特殊的类来应对特殊的各种情况。正所谓对症下药。下面给出了部分特殊类设计
一,请设计一个不能被拷贝的类
不能被拷贝的类我们在智能指针的unique_ptr已经初步认识过了,要想一个类不能被拷贝,只要想办法把它的拷贝构造和赋值重载干掉即可,如下代码:C++98和C++11的处理方式不同
class BanCopy
{
//C++98的处理方式
private:
/*BanCopy(const BanCopy&);
BanCopy& operator=(const BanCopy&);*/
public:
BanCopy(int a = 0)
:_a(a)
{
cout << "BanCopy()" << endl;
}
~BanCopy()
{
cout << "~BanCopy" << endl;
}
//C++11的处理方式
BanCopy(const BanCopy&) = delete;
BanCopy& operator=(const BanCopy&) = delete;
private:
int _a;
};
int main()
{
BanCopy a(1);
BanCopy b(a);
}
二,请设计一个只能在堆上创建对象的类
2.1 思路一:构造函数私有
class HeapOnly
{
public:
//提供一个公有的获取对象的方式,对象控制是new出来的
static HeapOnly* CreateObj()
{
return new HeapOnly;
}
//防拷贝
HeapOnly(const HeapOnly& hp) = delete;
HeapOnly& operator=(const HeapOnly& hp) = delete;
private:
HeapOnly()
:_a(0)
{}
private:
int _a;
};
//构造函数私有
void main3()
{
/*HeapOnly hp1;
static HeapOnly hp2;*/
//HeapOnly* hp3 = CreateObj();
//上面这一条语句是先有鸡还是先有蛋的问题,我们可以通过CreateObj来创建对象,但是我们只有先创建对象才能调用CreateObj
//把CreateObj定义成static就可以了
HeapOnly* hp3 = HeapOnly::CreateObj();
delete hp3;
//HeapOnly* copy(hp3);
}
2.2 思路二,析构函数私有
class HeapOnly
{
public:
/*static void Delete(HeapOnly* ptr)
{
delete ptr;
}*/
//两种方式都可以
void Delete()
{
delete this;
}
private:
~HeapOnly()
{
cout << "~HeapOnly" << endl;
}
private:
int _a;
};
//析构函数私有
void main()
{
/*HeapOnly hp1;
static HeapOnly hp2;*/
HeapOnly* ptr = new HeapOnly;
//HeapOnly::Delete(ptr);
ptr->Delete();
}
三,请设计一个只能在栈上创建对象的类
class StackOnly
{
public:
static StackOnly CreateObj()
{
StackOnly st;
return st;
}
// 不能防拷贝
//StackOnly(const StackOnly& st) = delete;
//StackOnly& operator=(const StackOnly& st) = delete;
void* operator new(size_t n) = delete;
private:
// 构造函数私有
StackOnly()
:_a(0)
{}
private:
int _a;
};
void main4()
{
StackOnly st1 = StackOnly::CreateObj();
//拷贝构造
static StackOnly copy2(st1);//本来不能拷贝的,用防拷贝,但是这样又会限制上面的CreateObj,不好处理,算是一个小缺陷
//StackOnly* copy = new StackOnly(st1); 要防止栈上的数据拷贝到堆上可以把new禁掉
}
四,请设计一个只能创建一个对象的类(单例模式)
4.1 关于设计模式
设计模式(Design Pattern)是一套被反复使用,多数人知晓的,经过分类的,代码设计经验的总结。而它产生的原因就好比我们中国古代兵法的产生,最开始各部落之间打仗就是拼人数,无技术含量的对砍,后来春秋战国后,各国也经常打仗,后来发现打仗也是有套路的,后来就有了《孙子兵法》
而使用设计模式的目的:提高代码可重用性,和可理解性,保证代码可靠性。设计模式使代码真正工程化,是软件工程的基石。
4.2 单例模式
一个类只能创建一个对象,即单例模式。该模式保证系统中该类只有一个实例化对象,并提供一个访问它的全局访问点,并且该实例被所有程序模块共享。
简单来说:保证一些数据(一个进程中)全局只有唯一一份,并且方便访问
①把这些数据放进一个类里面,把这个类设计出单例类
②封死构造和拷贝,提供一个static公有获取单例对象地函数
③如何创建单例对象,饿汉和懒汉
单例new对象释放问题:
①一般情况下,单例对象不需要释放的。因为一般整个程序运行期间都可能会用它。
单例对象在进程正常结束后,也会资源释放。
②有些特殊场景需要释放
4.3 饿汉模式
在main函数开始之前就创建出唯一对象
优点:代码简单,并且无线程安全问题,因为是在main函数之前创建的
缺点:
①一个程序中如果有多个单例需要被创建,并且这多个单例有创建顺序时,饿汉无法控制创建顺序
②饿汉单例类如果初始化任务多,会影响程序启动速度
class Singleton1
{
public:
//要保证每次获取的对象都是同一个对象,自己定义一个静态的自己类型的对象或指针
static Singleton1* GetInstance()
{
return _pinst;
}
void Add(const string& str)
{
_mtx.lock();
_v.push_back(str);
_mtx.unlock();
}
void Print()
{
_mtx.lock();
for (auto& e : _v)
{
cout << e << endl;;
}
cout << endl;
_mtx.unlock();
}
private:
//构造函数私有化,是为了限制在类外面随意创建对象
Singleton1(){}
Singleton1(const Singleton1& s) = delete;
Singleton1& operator=(const Singleton1& s) = delete;
private:
mutex _mtx;
vector<string> _v;
//static Singleton _inst;//声明
static Singleton1* _pinst; // 声明
};
//静态的必须在类外面初始化
//Singleton1 Singleton1::_inst = new Singleton1;
Singleton1* Singleton1::_pinst = new Singleton1;
//线程安全地往里面添加数据
void main()
{
srand(time(0));
int n = 100;
thread t1([n]() {
for (size_t i = 0; i < n; i++)
{
Singleton1::GetInstance()->Add("t1线程:" + to_string(rand()));
}
});
thread t2([n]() {
for (size_t i = 0; i < n; i++)
{
Singleton1::GetInstance()->Add("t2线程:" + to_string(rand()));
}
});
t1.join();
t2.join();
Singleton1::GetInstance()->Print();
}
4.4 懒汉模式
饿汉模式有程序启动时间增长的问题,所以我们可以用懒汉模式,它表示当我们第一次要使用对象时再创建实例对象
优点:①不需要控制创建顺序 ②不影响启动速度
缺点:①代码实现相比饿汉更复杂,这个在下面的代码中会深有体会 ②线程安全问题要处理好,因为如果不对临界区做保护会造成很严重的后果
class Singleton2
{
public:
static Singleton2* GetInstance()
{
//每次获取对象都要加锁解锁,但是我们只需要第一次new地时候加锁解锁
//双检查加锁
if (_pinst == nullptr) //这个时为了提供效率,不需要每次获取单例都加锁解锁
{
_imtx.lock();
if (_pinst == nullptr) //这个才是保证线程安全和只new一次
{
_pinst = new Singleton2;
}
_imtx.unlock();
}
return _pinst;
}
void Add(const string& str)
{
//添加数据时要保证线程安全
_vmtx.lock();
_v.push_back(str);
_vmtx.unlock();
}
void Print()
{
_vmtx.lock();
for (auto& e : _v)
{
cout << e << endl;;
}
cout << endl;
_vmtx.unlock();
}
class CGarbo
{
public:
~CGarbo()
{
if (_pinst)
{
delete _pinst;
_pinst = nullptr;
}
}
};
//上面那个是自动释放,我们也可以显示手动释放
static void DelInstance()
{
_imtx.lock();
if (_pinst)
{
delete _pinst;
_pinst = nullptr;
}
_imtx.unlock();
}
private:
//构造函数私有化,还有防拷贝
Singleton2(){}
Singleton2(const Singleton2& s) = delete;
Singleton2& operator=(const Singleton2& s) = delete;
private:
mutex _vmtx;
vector<string> _v;
static Singleton2* _pinst;//声明
static mutex _imtx;
};
//定义
Singleton2* Singleton2::_pinst = nullptr;
mutex Singleton2::_imtx;
//回收对象,main函数结构后,他会调用析构函数,就会释放单例对象
static Singleton2::CGarbo gc;
void main6()
{
srand(time(0));
int n = 100;
thread t1([n]() {
for (size_t i = 0; i < n; i++)
{
Singleton2::GetInstance()->Add("t1线程:" + to_string(rand()));
}
});
thread t2([n]() {
for (size_t i = 0; i < n; i++)
{
Singleton2::GetInstance()->Add("t2线程:" + to_string(rand()));
}
});
t1.join();
t2.join();
Singleton2::GetInstance()->Print();
}