特殊类设计

简介: 特殊类设计

设计一个类,不能被拷贝


拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,

只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。


C++98


将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。


class CopyBan
{
    // ...
private:
    CopyBan(const CopyBan&);
    CopyBan& operator=(const CopyBan&);
    //...
};


原因如下


设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了

只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。


C++11


C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上

=delete,表示让编译器删除掉该默认成员函数。


class CopyBan
{
    // ...
    CopyBan(const CopyBan&)=delete;
    CopyBan& operator=(const CopyBan&)=delete;
    //...
};

设计一个类,只能在堆上创建对象


实现方式:


将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。 提供一个成员函数,在该成员函数中完成堆对象的创建


class HeapOnly
{
public:
  HeapOnly* Create()
  {
  return new HeapOnly;
  }
private:
  HeapOnly()
  {}
  HeapOnly(const HeapOnly&)=delete;
};
int main()
{
  HeapOnly* py = HeapOnly::Create();
  return 0;
}


d9ee9677339c8b8b905804a97bda3d8b_4898a3e285da4c8180c0d3c2da6dbccb.png


通过结果来看,这里出现了新的问题:先有鸡还是先有蛋;没有对象怎么去调用函数,不调用函数又该怎么去创建对象呢???


将成员函数设置为静态可以完美解决这一问题,避免了this指针


1b857b641b9c964499127da22342ce5c_26904f0b09e34beb9d0c473c8790a5a4.png


设计一个类,只能在栈上创建对象


实现方式:

将构造函数私有化,然后设计静态方法创建对象返回即可。


class StackOnly
{
public:
  StackOnly static Creat()
  {
  return StackOnly();
  }
private:
  StackOnly()
  {}
};
int main()
{
  StackOnly so1 = StackOnly::Creat();
  return 0;
}


89d617bbd7af930bc357922c673201c9_79fc10431b6e44888d511963f7f03160.png


设计一个类,不能被继承


C++98


将构造函数私有化,派生类中调不到基类的构造函数。则无法继承


class NonInherit
{
public:
  static NonInherit GetInstance()
  {
  return NonInherit();
  }
private:
  NonInherit()
  {}
}


C++11


final关键字,final修饰类,表示该类不能被继承。


class A  final
{
    // ....
};


设计一个类,只能创建一个对象(单例模式)


单例模式

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个

访问它的全局访问点,该实例被所有程序模块共享。


单例模式有两种实现模式:


饿汉模式


程序一开始就创建对象

缺点:


如果单例对象初始化时数据太多,程序启动会变慢

如果多个单例模式初始化有依赖关系,饿汉模式将无法控制

class InfoSingleton
{
public:
  static InfoSingleton& GetInstance()
  {
  return _sins;
  }
  void Insert(string name, int salary)
  {
  _info[name] = salary;
  }
  void Print()
  {
  for (auto& e : _info)
  {
    cout << e.first << " " << e.second << endl;
  }
  cout << endl;
  }
private:
  InfoSingleton()
  {}
  InfoSingleton(const InfoSingleton&) = delete;
  InfoSingleton& operator=(const InfoSingleton&) = delete;
  map<string, int> _info;
private:
  static InfoSingleton _sins;
};
//在程序入口之前就完成单例对象的初始化
InfoSingleton InfoSingleton::_sins;
int main()
{
  InfoSingleton& info = InfoSingleton::GetInstance();
  info.Insert("张三", 15000);
  info.Insert("李四", 14000);
  info.Insert("王五", 11000);
  info.Insert("赵六", 13000);
  info.Insert("孙七", 12000);
  info.Print();
  return 0;
}


懒汉模式


第一次获取单例对象时创建


对象在主函数之后才会创建,不会影响启动顺序

可以主动控制创建顺序

//RAII锁管理类
template<class Lock>
class LockGuard
{
public:
  LockGuard(Lock& lk)
  :_lk(lk)
  {
  _lk.lock();
  }
  ~LockGuard()
  {
  _lk.unlock();
  }
private:
  Lock& _lk;
};
class InfoSingleton
{
public:
  //多个线程同时调用GetInstance,存在线程安全问题
  static InfoSingleton& GetInstance()
  {
  //第一次获取单例对象的时候创建对象
  //双检查加锁
  //第一次对象创建之后,避免每次都加锁的检查,提高性能
  if (_psins == nullptr)
  {
    _smtx.lock();
    try
    {
    //保存线程安全并且只创建一个单例对象
    if (_psins == nullptr)
    {
      _psins = new InfoSingleton;
    }
    }
    catch (...)
    {
    _smtx.unlock();
    throw;
    }
    _smtx.unlock();
  }
  return *_psins;
  }
  //单例对象一般不需要释放
  //单例对象不使用时,必须手动处理
  static void DelInstance()
  {
  LockGuard<mutex> lock(_smtx);
  if (_psins)
  {
    delete _psins;
    _psins = nullptr;
    cout << "DelInstance()" << endl;
  }
  }
  void Insert(string name, int salary)
  {
  _info[name] = salary;
  }
  void Print()
  {
  for (auto& e : _info)
  {
    cout << e.first << " " << e.second << endl;
  }
  cout << endl;
  }
private:
  InfoSingleton()
  {}
  InfoSingleton(const InfoSingleton&) = delete;
  InfoSingleton& operator=(const InfoSingleton&) = delete;
  map<string, int> _info;
private:
  static InfoSingleton* _psins;
  static mutex _smtx;
};
InfoSingleton* InfoSingleton::_psins = nullptr;
mutex InfoSingleton::_smtx;
int main()
{
  InfoSingleton& info = InfoSingleton::GetInstance();
  info.Insert("张三", 10000);
  info.Insert("李四", 15000);
  info.Insert("王五", 13000);
  info.Insert("赵六", 11000);
  info.Insert("孙七", 12000);
  info.Print();
  info.DelInstance();
  return 0;
}

ed0842a5564fd6a187168d765d798427_748019fc4b234cef9df522eddd709e37.png


目录
相关文章
|
6月前
|
设计模式 安全 Java
【C++】特殊类设计
【C++】特殊类设计
|
4月前
|
设计模式 安全 编译器
【C++11】特殊类设计
【C++11】特殊类设计
58 10
|
12月前
|
设计模式 安全 Java
|
5月前
|
程序员 C语言 C++
【C++语言】继承:类特性的扩展,重要的类复用!
【C++语言】继承:类特性的扩展,重要的类复用!
|
6月前
|
算法 编译器 C语言
【C/C++ 编程题 01】用C++设计一个不能被继承的类
【C/C++ 编程题 01】用C++设计一个不能被继承的类
65 0
|
6月前
|
编译器 C++
【C++】—— 特殊类设计
【C++】—— 特殊类设计
|
6月前
|
设计模式 Java C++
C++之特殊类的设计
C++之特殊类的设计
25 0
|
11月前
|
设计模式 安全 编译器
C++特殊类设计
C++特殊类设计
|
编译器 C++
【C++】特殊类设计(上)
【C++】特殊类设计(上)
|
设计模式 安全 Java
【C++】特殊类设计(下)
【C++】特殊类设计(下)