目录
1、请设计一个类,不能被拷贝
// C++98
// 只声明,不实现
class CopyBan
{
// ...
private:
CopyBan(const CopyBan&);
CopyBan& operator=(const CopyBan&);
//...
};
// C++11
// delete
class CopyBan
{
// ...
CopyBan(const CopyBan&)=delete;
CopyBan& operator=(const CopyBan&)=delete;
//...
};
2、请设计一个类,只能在堆上创建对象
赋值重载不用禁掉,因为赋值肯定是给已经存在的对象进行赋值。
既然已经不能在栈上创建对象了,那么就算赋值也是赋值的堆上的对象。
1.封构造函数
class HeapOnly
{
public:
// 这里必须写成静态的
// 当我们把构造给写成私有的了,就不能构造出对象了
// 因此只能通过类调用该函数
// 将它设为static让它属于整个类
static HeapOnly* CreateObj()
{
return new HeapOnly;
}
private:
HeapOnly() // 设为私有,禁止外部构造
{}
// 禁止拷贝构造
HeapOnly(const HeapOnly& hp) = delete;
};
int main()
{
HeapOnly* hp = HeapOnly::CreateObj();
//HeapOnly hp1(*hp); // 拷贝构造禁止之后这句就要报错
return 0;
}
2.封析构函数
//封析构函数
class HeapOnly
{
public:
HeapOnly()
{}
// 由于析构设为了私有
// 所以要自己实现一个接口来调用它
void Destroy()
{
this->~HeapOnly();
}
private:
// 将析构设为私有
~HeapOnly()
{}
// 禁止拷贝构造
HeapOnly(const HeapOnly& hp) = delete;
};
int main()
{
//HeapOnly hp1;
HeapOnly* php1 = new HeapOnly;
//可以直接用对象来调用Destroy
php1->Destroy();
return 0;
}
3、请设计一个类,只能在栈上创建对象
//只能在栈上创建对象
class StackOnly
{
public:
// 调用构造函数
static StackOnly CreateObj()
{
return StackOnly();
}
void Print()
{
cout << "StackOnly::Print()" << endl;
}
private:
// 将构造设为私有
StackOnly()
{}
};
int main()
{
// 仍然可以调用CeateObj来在栈上创建对象
StackOnly so1 = StackOnly::CreateObj();
// 但是防不住生成静态对象
// static StackOnly so4 = StackOnly::CreateObj();
StackOnly::CreateObj().Print();
const StackOnly& so4 = StackOnly::CreateObj();
so4.CreateObj();
// 这样就不能创建静态的对象了
// static StackOnly so2;
// 也不能在堆上创建对象了
// StackOnly*pso3=new StackOnly;
return 0;
}
4、请设计一个类,不能被继承
// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit()
{}
};
// C++11方法
// final关键字,final修饰类,表示该类不能被继承。
class A final
{
// ....
};
5、请设计一个类,只能创建一个对象(单例模式)
单例模式概念
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
饿汉模式
//单例模式的类:全局只有一个唯一对象
//饿汉模式: main函数执行之前就创建对象
class InfoSingleton
{
public:
static InfoSingleton& GetInstance()
{
//返回我们在类内创建的对象就可以了
return _sins;
}
void Insert(string name, int salary)
{
_info[name] = salary;
}
void Print()
{
for (auto kv : _info)
{
cout << kv.first << " " << kv.second << endl;
}
}
private:
// 为了避免允许创建多个对象
// 因此需要把构造函数给封了
InfoSingleton()
{}
//拷贝构造和赋值也要设为私有,以防通过拷贝和赋值搞出新对象
InfoSingleton(const InfoSingleton& info);
InfoSingleton& operator=(const InfoSingleton& info);
map<string, int> _info;
//在类内定义了一个类类型的
private:
// 不能在类外调用构造函数,但是可以再类里面调用
static InfoSingleton _sins;
};
// 第一个是类型 第二个是作用域
InfoSingleton InfoSingleton::_sins;
int main()
{
InfoSingleton::GetInstance().Insert("张三", 10000);
InfoSingleton& infosl = InfoSingleton::GetInstance();
infosl.Insert("李四", 15000);
infosl.Insert("赵六", 12000);
infosl.Insert("王五", 15000);
infosl.Insert("李四", 15000);
infosl.Print();
//InfoSingleton copy = InfoSingleton::GetInstance();
//copy.Insert("孙7", 50000);
//copy.Print();
//infosl.Print();
return 0;
}
注意:
注意:
主要就是在类中定义的那个静态的类类型的对象,使外部可以使用类域来调用类内这个对象,并且可以调用该类的构造函数。
缺点
1、单例对象初始化时数据太多,导致启动慢,因为它在main函数之前就要初始化。
2、多个单例类有初始化依赖关系,饿汉模式无法控制
举例:
A和B都是单例类,要求先初始化A,再初始化B,因为B会依赖A
但是饿汉模式无法控制顺序
懒汉模式
// 使用RAII的锁管理方式
//在这里我们手写了一个,其实库里面是有这个的
template<class Lock>
class LockGuard
{
public:
LockGuard(Lock& lk)
:_lk(lk)
{
_lk.lock();
}
~LockGuard()
{
_lk.unlock();
}
private:
// 引用的成员变量必须在初始化列表进行初始化
mutex& _lk;
};
//懒汉模式:第一次获取单例对象的时候创建对象
//1、对象再main函数之后才会创建,不会影响启动顺序
//2、可以主动控制创建顺序
class InfoSingleton
{
public:
// 多个线程一起调用GetInstance,存在线程安全的风险
// 所以需要加锁
static InfoSingleton& GetInstance()
{
//第一次获取单例对象的时候创建对象
//双检查加锁
//对象new出来以后,避免每次都加锁的检查,可以提高性能
if (_psins == nullptr)
{
// 使用RAII的锁管理方式
LockGuard<mutex> lock(_smtx);
//这个是库里面的
//std::lock_guard<mutex> lock(_smtx);
// 为了保证线程安全且只new一次
if (_psins == nullptr)
{
_psins = new InfoSingleton;
}
}
return *_psins;
}
//一般单例对象不需要考虑释放
//单例对象不用时,必须手动处理,一些资源需要保存
//可以手动调用主动回收
//也可以让他自己在程序结束时,自动回收
//可以手动调用主动回收
static void DelInstance()
{
// 保存数据到文件
// ...
lock_guard<mutex> lock(_smtx);
if (_psins)
{
delete _psins;
_psins = nullptr;
}
}
//也可以让他自己在程序结束时,自动回收
//内部类
class GC
{
public:
~GC()
{
//内部类是外部类的友元
//因此我们可以直接调用
if (_psins)
{
cout << "~GC()" << endl;
DelInstance();
}
}
};
void Insert(string name, int salary)
{
_info[name] = salary;
}
void Print()
{
for (auto kv : _info)
{
cout << kv.first << " : " << kv.second << endl;
}
}
private:
// 为了避免允许创建多个对象
// 因此需要把构造函数给封了
InfoSingleton()
{}
//拷贝构造和赋值也要设为私有,以防通过拷贝和赋值搞出新对象
InfoSingleton(const InfoSingleton& info);
InfoSingleton& operator=(const InfoSingleton& info);
map<string, int> _info;
//在类内定义了一个类类型的
private:
// 不能在类外调用构造函数,但是可以再类里面调用
static InfoSingleton* _psins;
static mutex _smtx;
// 这个局部对象出了作用域救护调用对应的析构函数
static GC _gc;
};
// 第一个是类型 第二个是作用域
InfoSingleton* InfoSingleton::_psins = nullptr;
mutex InfoSingleton::_smtx;
InfoSingleton::GC InfoSingleton::_gc;
int main()
{
InfoSingleton::GetInstance().Insert("张三", 10000);
InfoSingleton& infosl = InfoSingleton::GetInstance();
infosl.Insert("李四", 15000);
infosl.Insert("赵六", 12000);
infosl.Insert("王五", 15000);
infosl.Insert("李四", 15000);
infosl.Print();
//InfoSingleton copy = InfoSingleton::GetInstance();
//copy.Insert("孙7", 50000);
//copy.Print();
//infosl.Print();
InfoSingleton::DelInstance();
return 0;
}