欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 会展 > C++特殊类设计

C++特殊类设计

2025/3/10 15:10:02 来源:https://blog.csdn.net/Talon7/article/details/145963060  浏览:    关键词:C++特殊类设计

C++特殊类设计

  • 1、请设计一个类,不能被拷贝
  • 2、请设计一个类,只能在堆上创建对象
  • 3、请设计一个类,只能在栈上创建对象
  • 4、请设计一个类,不能被继承
  • 5、请设计一个类,只能创建一个对象(单例模式)
    • 5.1、饿汉模式
    • 5.2、懒汉模式

1、请设计一个类,不能被拷贝

拷贝只会发生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
C++98方式:将拷贝构造和赋值私有

class CopyBan
{
public:CopyBan() {}
private:CopyBan(const CopyBan& cb);CopyBan& operator=(const CopyBan& cb);
};

1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。


C++11方式:扩展delete用法

class CopyBan
{
public:CopyBan() {}CopyBan(const CopyBan& cb) = delete;CopyBan& operator=(const CopyBan& cb) = delete;
};

C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。


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

方式一:析构函数私有

class HeapOnly
{
public:void Destroy() {delete this;}
private:~HeapOnly(){}
};
HeapOnly* hp = new HeapOnly;
hp->Destroy();

在栈上创建的对象除了作用域会自动调用析构函数,析构函数私有就无法调用了,所以无法在栈上创建对象。
可以在堆上创建对象,但是有个问题,delete的时候无法调用析构函数,因为delete的原理是析构函数+释放空间,所以我们提供一个Destroy接口,直接在函数内部delete,这样就可以调用析构函数。


方式二:构造函数私有

class HeapOnly
{
public:static HeapOnly* CreateObj(){return new HeapOnly;}
private:HeapOnly(){}HeapOnly(const HeapOnly& hp) = delete;HeapOnly& operator=(const HeapOnly& hp) = delete;
};
HeapOnly* hp = HeapOnly::CreateObj();

构造函数私有后,既不能在栈上创建,也不能在堆上创建。因此我们需要提供一个CreateObj函数,在里面创建然后将指针返回给外部接收,并且这个函数必须是静态的,否则调不到。

但是这样还是防不住下面这种方式:

HeapOnly* hp = HeapOnly::CreateObj();
HeapOnly copy(*hp);

所以还需要防拷贝。


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

方式:构造函数私有

class StackOnly
{
public:static StackOnly CreateObj(){StackOnly st;return st;}
private:StackOnly(){}void* operator new(size_t size) = delete;
};
StackOnly st1 = StackOnly::CreateObj();

构造函数私有,然后提供一个静态成员函数,在该成员函数里面创建对象返回。
但是还是防不住下面这种情况:

StackOnly* hp = new StackOnly(st1);

new的原理就是operator new加构造函数,所以我们需要禁掉operator new。


4、请设计一个类,不能被继承

C++98方式:构造函数私有

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

C++11方式:final修饰

class A final
{
// ...
};

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

单例模式:
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

单例模式有两种实现模式:饿汉模式和懒汉模式。

5.1、饿汉模式

之前的CreateObj,不管是new还是栈上创建再返回,都是不同的对象,现在需要的是同一个对象,思考一下该怎么做?

namespace hungry
{class Singleton{public:static Singleton& GetInstance(){return _sinst;}void Add(const pair<string, string>& kv){_dict[kv.first] = kv.second;}void Print(){for (const auto& e : _dict){cout << e.first << ":" << e.second << endl;}cout << endl;}private:Singleton(){}Singleton(const Singleton& s) = delete;Singleton& operator=(const Singleton& s) = delete;map<string, string> _dict;// ...static Singleton _sinst;};Singleton Singleton::_sinst;
}int main()
{cout << &hungry::Singleton::GetInstance() << endl;cout << &hungry::Singleton::GetInstance() << endl;cout << &hungry::Singleton::GetInstance() << endl;hungry::Singleton::GetInstance().Add({ "xxx", "111" });hungry::Singleton::GetInstance().Add({ "yyy", "222" });hungry::Singleton::GetInstance().Add({ "zzz", "333" });hungry::Singleton::GetInstance().Print();return 0;
}

在这里插入图片描述
实现:
1、首先将构造函数私有。
2、声明静态类对象,然后在类外定义,提供一个获取对象的静态成员函数GetInstance()。
3、防拷贝,将拷贝和赋值声明为删除。

饿汉模式:就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。
但是也有下面的问题:
1、如果单例对象初始化内容很多,影响启动速度。
2、如果有两个单例类,相互依赖关系。假设有A、B两个单例类,要求先创建A,然后再创建B,B的初始化依赖A。


5.2、懒汉模式

namespace lazy
{class Singleton{public:static Singleton& GetInstance(){if (_psinst == nullptr){_psinst = new Singleton;}return *_psinst;}static void DelInstance(){if (_psinst){delete _psinst;_psinst = nullptr;}}void Add(const pair<string, string>& kv){_dict[kv.first] = kv.second;}void Print(){for (const auto& e : _dict){cout << e.first << ":" << e.second << endl;}cout << endl;}private:Singleton(){}Singleton(const Singleton& s) = delete;Singleton& operator=(const Singleton& s) = delete;~Singleton(){cout << "~Singleton()" << endl;// map数据写到文件中FILE* fin = fopen("map.txt", "w");for (auto& e : _dict){fputs(e.first.c_str(), fin);fputs(":", fin);fputs(e.second.c_str(), fin);fputs("\n", fin);}}map<string, string> _dict;// ...static Singleton* _psinst;};Singleton* Singleton::_psinst = nullptr;
}

懒汉模式:第一次调用GetInstance的时候创建单例对象
那么如何释放呢?单例一般不用释放,进程结束直接带走了。
特殊场景:1、中途需要显示释放 2、程序结束时,需要做一些特殊动作(如持久化)

上面的析构函数就是场景2,假设需要把数据写入文件,那么如果是进程结束是不会走析构函数的,无法做到数据持久化。
所以可以提供一个DelIstance函数,中途可以显示释放:

cout << &lazy::Singleton::GetInstance() << endl;
cout << &lazy::Singleton::GetInstance() << endl;
cout << &lazy::Singleton::GetInstance() << endl;
lazy::Singleton::GetInstance().Add({ "xxx", "111" });
lazy::Singleton::GetInstance().Add({ "yyy", "222" });
lazy::Singleton::GetInstance().Add({ "zzz", "333" });
lazy::Singleton::GetInstance().Print();
lazy::Singleton::GetInstance().DelInstance();

在这里插入图片描述


那么如果我不想显示释放,并且还希望数据持久化呢?
可以写这么一个类:

class GC
{
public:~GC(){lazy::Singleton::GetInstance().DelInstance();}
};GC gc;

还可以直接写在Singleton内部:

namespace lazy
{class Singleton{public:static Singleton& GetInstance(){if (_psinst == nullptr){_psinst = new Singleton;}return *_psinst;}static void DelInstance(){if (_psinst){delete _psinst;_psinst = nullptr;}}void Add(const pair<string, string>& kv){_dict[kv.first] = kv.second;}void Print(){for (const auto& e : _dict){cout << e.first << ":" << e.second << endl;}cout << endl;}class GC{public:~GC(){lazy::Singleton::GetInstance().DelInstance();}};private:Singleton(){}Singleton(const Singleton& s) = delete;Singleton& operator=(const Singleton& s) = delete;~Singleton(){cout << "~Singleton()" << endl;// map数据写到文件中FILE* fin = fopen("map.txt", "w");for (auto& e : _dict){fputs(e.first.c_str(), fin);fputs(":", fin);fputs(e.second.c_str(), fin);fputs("\n", fin);}}map<string, string> _dict;// ...static Singleton* _psinst;static GC _gc;};Singleton* Singleton::_psinst = nullptr;Singleton::GC Singleton::_gc;
}int main()
{cout << &lazy::Singleton::GetInstance() << endl;cout << &lazy::Singleton::GetInstance() << endl;cout << &lazy::Singleton::GetInstance() << endl;lazy::Singleton::GetInstance().Add({ "xxx", "111" });lazy::Singleton::GetInstance().Add({ "yyy", "222" });lazy::Singleton::GetInstance().Add({ "zzz", "333" });lazy::Singleton::GetInstance().Print();return 0;
}

在这里插入图片描述
在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词