一、请设计一个类,不能被拷贝
其实就是防止拷贝构造和赋值运算符的重载,这个在C++11中讲了,在C++98之前可以声明为private,现在直接等于delete就可以了
//C++98
class A {
public:A(){}
private:A(const A& a);A& operator=(const A& a);
};
class A {
public:A() {}A(const A& a) = delete;A& operator=(const A& a) = delete;
};
二、请设计一个类,只能在堆上创建对象
实现方式:
第一种:
- 将类的构造函数私有,拷贝构造关掉。防止别人调用拷贝在栈上生成对象。
- 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建,一定是静态的奥,只有静态才可以::访问
第二种:
将析构函数设置成私有,这样普通对象无法创建,new出来的对象只有delete了才会去调析构函数,那我们可以额外开一个函数来析构。
但是这样是不还有问题啊,HeapOnly *a1 = a3;这样a1是不还在栈上啊,所以这个是伪的,因为有构造函数才会new,构造函数关不了,所以不稳妥。
所以第一种是最稳妥的,把所有方式都关了。
第一种方法:原理简单说一下:create()函数调用new,new需要调用operator new + 构造函数,构造函数是私有,类内可见,所以就可以创建对象了
class HeapOnly {public:void Destroy() {delete this;}static HeapOnly* create(){return new HeapOnly;}
private://~HeapOnly() {}HeapOnly() {}HeapOnly(const HeapOnly& p) = delete;HeapOnly& operator=(const HeapOnly& p) = delete;
};
int main()
{HeapOnly* a3 = HeapOnly:: create();return 0;
}
第二种方法:
class HeapOnly {public:void Destroy() {delete this;}
private:~HeapOnly() {}
};
int main()
{//HeapOnly a;HeapOnly *a1 = new HeapOnly;//static HeapOnly a2;//delete a1;a1->Destroy();
}
三、请设计一个类,只能在栈上创建对象
方法:和上面的类似
实现方式:
- 将类的构造函数私有,拷贝构造声明成私有。关掉operator new,因为拷贝构造不能关,返回一个静态的成员调用的就是拷贝构造,但是这样还得防止new一个对象,所以只能关operator new
- 提供一个静态的成员函数,在该静态成员函数中完成栈对象的创建,一定是静态的奥,只有静态才可以::访问
class StackOnly {public:static StackOnly CreateStack(){StackOnly a;return a;}void* operator new(size_t size) = delete;
private:StackOnly(){}//StackOnly(const StackOnly& p) = delete;//StackOnly& operator=(const StackOnly& p) = delete;
};int main()
{StackOnly a1 = StackOnly::CreateStack();StackOnly a1 = StackOnly::CreateStack();//static StackOnly a3;//StackOnly* p = new StackOnly(a1);//StackOnly* p3 = new StackOnly(a1);//p3仍在堆上return 0;
}
有一点问题,还是防止不了在静态区创建对象
static StackOnly a1 = StackOnly::CreateStack();
四、请设计一个类,不能被继承
C++98之前就是把构造函数私有,这样继承的时候无法调用基类的构造函数,就会无法继承(只是创建不了对象,在继承的时候不会报错)
C++11后利用final关键字直接更简洁
class Base final {
private:Base() {}
};class Derive : public Base {
};
这种会直接报错
五、请设计一个类,只能创建一个对象(单例模式)
什么是单例模式呢?
特点:
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置
息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
实现:
错误思路(不正确,但是可以实现效果)
class Singleton
{
private:static int p;
public:Singleton(){assert(p > 0);p--;}Singleton(const Singleton& p) = delete;Singleton& operator=(const Singleton& p) = delete;
};
int Singleton::p = 1;
就是存一个static变量p,在调用构造函数的时候去–,这样第二次就无法创建对象了,但是这样如果写两个对象,程序直接由于断言就崩了,所以这样写不对,也别去学。(属于是胡思乱想的结果罢了)
实现
先说一下大体的思路,你构造函数和赋值一定要关的,这没啥说的,构造函数私有,提供一个返回单例对象的接口,但是你构造函数关了,所以要搞一个静态私有成员,访问对象时只通过这个接口来访问,只有一个对象
单例模式有两种实现模式:1、饿汉模式:不管你用不用,程序启动时就创建一个唯一的实例对象
实现:
class Singleton {
public://提供一个创建单例对象的函数static Singleton& getInstance(){return _sinst;}void add(const pair<int, int>& p){mp[p.first] = p.second;}void Print(){for (const auto& p : mp){cout << p.first << ' ' << p.second << endl;}}
private:Singleton() {}static Singleton _sinst;Singleton& operator=(const Singleton& p) = delete;Singleton(const Singleton& p) = delete;map<int, int> mp;
};
Singleton Singleton::_sinst;
优点:简单
缺点:可能会导致进程启动慢(比如要初始化很多东西),且如果有多个单例类对象实例启动顺序不确定(如果一个类依靠另一个类来初始化,启动顺序还不确定,就不好)。
2.懒汉模式
如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。
怎么实现呢?和上面的类似,只不过我存一个指针,在调用这个函数的时候再初始化,这样就不会不用也创建对象了。
class Singleton {public://提供一个创建单例对象的函数static Singleton& getInstance(){if (_sinst == nullptr){//第一次调用这个函数时才创建单例对象_sinst = new Singleton;}return *_sinst;}void add(const pair<string, string>& p){mp[p.first] = p.second;}void Print(){for (const auto& p : mp){cout << p.first << ' ' << p.second << endl;}}private:Singleton() {}static Singleton* _sinst;Singleton& operator=(const Singleton& p) = delete;Singleton(const Singleton& p) = delete;map<string, string> mp;};Singleton* Singleton::_sinst = nullptr;
到这就完事了吗?当然没有,你new出来的对象当然要析构啊,这里再推荐两种方式(析构函数一定不行,因为你实例化的对象是个函数接口,生命周期结束不会自动调用析构函数)
1.搞一个del函数,注意要搞成静态的,不然没法访问,但是这样需要主动调用,我不想主动调用怎么办,请看下一个方法
static void Del()
{if (_sinst != nullptr){delete _sinst;_sinst = nullptr;}
}
2.在加一个类,该类的析构函数调用Del()
class GC {
public:~GC(){Singleton::getInstance().Del();}
};
GC gc;
这样生命周期结束时就会调用Del();
还可以这样搞一个
class GC {public:~GC(){lazy::Singleton::getInstance().Del();cout << "GC" << endl;}};static GC _gc;
他分装在单例类的内部(记得要初始化)(Singleton* Singleton::_sinst = nullptr;)
原理类似,静态成员一直存在,结束调用这个类的析构,这样既可以做到如果我不希望中间某个时候析构,让他自己生命周期结束可以析构,也可以我如果不想用了直接主动的调用析构(del())。
总结
本次内容不难,如果看着很费劲建议去多学学构造函数,拷贝构造函数,普通对象和new出来的对象,new的原理啊等等,这些东西掌握了,以上内容还是很好理解的。