做网站开发需要培训吗,在哪个网站做推广效果更佳,有哪些做任务网站,做视频网站需要什么资质请设计一个类#xff0c;只能在堆上创建对象
方式如下#xff1a;
将构造函数设置为私有#xff0c;防止外部直接调用构造函数在栈上创建对象。向外部提供一个获取对象的static接口#xff0c;该接口在堆上创建一个对象并返回。将拷贝构造函数设置为私有#xff0c;并且…请设计一个类只能在堆上创建对象
方式如下
将构造函数设置为私有防止外部直接调用构造函数在栈上创建对象。向外部提供一个获取对象的static接口该接口在堆上创建一个对象并返回。将拷贝构造函数设置为私有并且只声明不实现防止外部调用拷贝构造函数在栈上创建对象。
class HeapOnly
{
public://2、提供一个获取对象的接口并且该接口必须设置为静态成员函数static HeapOnly* CreateObj(){return new HeapOnly;}
private://1、将构造函数设置为私有HeapOnly(){}//3、将拷贝构造函数设置为私有并且只声明不实现//C98HeapOnly(const HeapOnly);//C11//HeapOnly(const HeapOnly) delete;
};说明一下
向外部提供的CreateObj函数必须设置为静态成员函数因为外部调用该接口就是为了获取对象的而非静态成员函数必须通过对象才能调用。C98通过将拷贝构造函数声明为私有以达到防拷贝的目的C11可以在拷贝构造函数后面加上delete表示让编译器将拷贝构造函数删除此时也能达到防拷贝的目的。
举例
class HeapOnly
{
public:static HeapOnly* createObj(){return new HeapOnly;}
private://1、将构造函数设置为私有HeapOnly():_a(0){}//3、将拷贝构造函数设置为私有并且只声明不实现//C98HeapOnly(const HeapOnly);//C11//HeapOnly(const HeapOnly) delete;
private:int _a;};
int main()
{HeapOnly ho1;//栈上//HeapOnly* ptr HeapOnly::createObj();//HeapOnly copy(*ptr);cout ptr endl;return 0;
}当我们想通过HeapOnly ho1;在外部直接调用构造函数在栈上创建对象时我们看到构造函数为私有防止了我们在栈上创建对象。
因为构造函数私有的关系我们只能通过提供的获取对象的static接口createObj()该接口在堆上创建一个对象并返回以此来获取堆上的对象。
HeapOnly* ptr HeapOnly::createObj();请设计一个类只能在栈上创建对象 方法一同上将构造函数私有化然后设计静态方法创建对象返回即可 将构造函数设置为私有防止外部直接调用构造函数在堆上创建对象。向外部提供一个获取对象的static接口该接口在栈上创建一个对象并返回。
class StackOnly
{
public://2、提供一个获取对象的接口并且该接口必须设置为静态成员函数static StackOnly CreateObj(){return StackOnly();}
private://1、将构造函数设置为私有StackOnly():_a(0){}
private:int _a;
};但该方法有一个缺陷就是无法防止外部调用拷贝构造函数创建对象。
StackOnly obj1 StackOnly::CreateObj();
static StackOnly obj2(obj1); //在静态区拷贝构造对象
StackOnly* ptr new StackOnly(obj1); //在堆上拷贝构造对象但是我们不能将构造函数设置为私有也不能用delete的方式将拷贝构造函数删除因为CreateObj函数当中创建的是栈上局部对象返回局部对象的过程中势必需要调用拷贝构造函数。 方法二屏蔽operator new函数和operator delete函数。 class StackOnly
{
public://2、提供一个获取对象的接口并且该接口必须设置为静态成员函数static StackOnly CreateObj(){return StackOnly();}private:void* operator new(size_t size) delete;void operator delete(void* p) delete;//1、将构造函数设置为私有StackOnly():_a(0){}
private:int _a;
};
new和delete的原理
new在堆上申请空间实际分为两步第一步是调用operator new函数申请空间第二步是在申请的空间上执行构造函数完成对象的初始化工作。delete在释放堆空间也分为两步第一步是在该空间上执行析构函数完成对象中资源的清理工作第二步是调用operator delete函数释放对象的空间。
new和delete默认调用的是全局的operator new函数和operator delete函数但如果一个类重载了专属的operator new函数和operator delete函数那么new和delete就会调用这个专属的函数。所以只要把operator new函数和operator delete函数屏蔽掉那么就无法再使用new在堆上创建对象了。
但该方法也有一个缺陷就是无法防止外部在静态区创建对象。
static StackOnly obj; //在静态区创建对象请设计一个类不能被拷贝
要让一个类不能被拷贝就要让该类不能调用拷贝构造函数和赋值运算符重载函数因此直接将该类的拷贝构造函数和赋值运算符重载函数设置为私有或者用C11的方式将这两个函数删除即可。
class CopyBan
{
public:CopyBan(){}
private://C98//CopyBan(const CopyBan);//CopyBan operator(const CopyBan);//C11CopyBan(const CopyBan) delete;CopyBan operator(const CopyBan) delete;
};请设计一个类不能被继承
方法一C98
将该类的构造函数设置为私有即可因为子类的构造函数被调用时必须调用父类的构造函数初始化父类的那一部分成员但父类的私有成员在子类当中是不可见的所以在创建子类对象时子类无法调用父类的构造函数对父类的成员进行初始化因此该类被继承后子类无法创建出对象。
class NonInherit
{
public:static NonInherit CreateObj(){return NonInherit();}
private://将构造函数设置为私有NonInherit(){}
};方法二C11
C98的这种方式其实不够彻底因为这个类仍然可以被继承编译器不会报错只不过被继承后无法实例化出对象而已。于是C11中提供了final关键字被final修饰的类叫做最终类最终类无法被继承此时就算继承后没有创建对象也会编译出错。
class NonInherit final
{//...
};请设计一个类只能创建一个对象单例模式
什么是单例模式
单例模式是一种设计模式Design Pattern设计模式就是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式的目的就是为了可重用代码、让代码更容易被他人理解、保证代码可靠性程序的重用性。单例模式指的就是一个类只能创建一个对象该模式可以保证系统中该类只有一个实例并提供一个访问它的全局访问点该实例被所有程序模块共享。比如在某个服务器程序中该服务器的配置信息存放在一个文件中这些配置数据由一个单例对象同一读取然后服务进程中的其他对象再通过这个单例对象获取这些配置信息这种方式简化了在复杂环境下的配置管理。
单例模式有两种实现方式分别是饿汉模式和懒汉模式
饿汉模式
单例模式的饿汉实现方式如下
将构造函数设置为私有并将拷贝构造函数和赋值运算符重载函数设置为私有或删除防止外部创建或拷贝对象。提供一个指向单例对象的static指针并在程序入口之前完成单例对象的初始化。提供一个全局访问点获取单例对象。
class Singleton
{
public://3、提供一个全局访问点获取单例对象static Singleton* GetInstance(){return _inst;}
private://1、将构造函数设置为私有并防拷贝Singleton(){}Singleton(const Singleton) delete;Singleton operator(const Singleton) delete;//2、提供一个指向单例对象的static指针static Singleton* _inst;
};//在程序入口之前完成单例对象的初始化
Singleton* Singleton::_inst new Singleton;线程安全相关问题
饿汉模式在程序运行主函数之前就完成了单例对象的创建由于main函数之前是不存在多线程的因此饿汉模式下单例对象的创建过程是线程安全的。后续所有多线程要访问这个单例对象都需要通过调用GetInstance函数来获取这个获取过程是不需要加锁的因为这是一个读操作。当然如果线程通过GetInstance获取到单例对象后要用这个单例对象进行一些线程不安全的操作那么这时就需要加锁了。
懒汉模式
单例模式的懒汉实现方式如下
将构造函数设置为私有并将拷贝构造函数和赋值运算符重载函数设置为私有或删除防止外部创建或拷贝对象。提供一个指向单例对象的static指针并在程序入口之前先将其初始化为空。提供一个全局访问点获取单例对象。
class Singleton
{
public://3、提供一个全局访问点获取单例对象static Singleton* GetInstance(){//双检查if (_inst nullptr){_mtx.lock();if (_inst nullptr){_inst new Singleton;}_mtx.unlock();}return _inst;}
private://1、将构造函数设置为私有并防拷贝Singleton(){}Singleton(const Singleton) delete;Singleton operator(const Singleton) delete;//2、提供一个指向单例对象的static指针static Singleton* _inst;static mutex _mtx; //互斥锁
};//在程序入口之前先将static指针初始化为空
Singleton* Singleton::_inst nullptr;
mutex Singleton::_mtx; //初始化互斥锁线程安全相关问题
懒汉模式在程序运行之前没有进行单例对象的创建而是等到某个线程需要使用这个单例对象时再进行创建也就是GetInstance函数第一次被调用时创建单例对象。因此在调用GetInstance函数获取单例对象时需要先判断这个static指针是否为空如果为空则说明这个单例对象还没有创建此时需要先创建这个单例对象然后再将单例对象返回。GetInstance函数第一次调用时需要对static指针进行写入操作这个过程不是线程安全的因为多个线程可能同时调用GetInstance函数如果不对这个过程进行保护此时这多个线程就会各自创建出一个对象。
双检查加锁
对GetInstance函数中创建单例对象的过程进行保护本质就是需要引入互斥锁最简单的加锁方式就是在进行if判断之前加锁在整个if语句之后进行解锁。但实际只有GetInstance函数第一次被调用创建单例对象时需要使用互斥锁进行保护而后续调用GetInstance函数获取单例对象只是一个读操作是不需要使用互斥锁进行保护的。如果简单的将加锁解锁操作放到if语句前后那么在后续调用GetInstance函数获取已经创建好的单例对象时就会进行大量无意义的加锁解锁操作导致线程不断切入切出进而影响程序运行效率。对于这种只有第一次需要加锁保护的场景可以使用双检查加锁双检查就是在当前加锁和解锁的外面再进行一次if判断判断static指针是否为空。这样一来后续调用GetInstance函数获取已经创建好的单例对象时外层新加的if判断就会起作用这样就避免了后续无意义的加锁解锁操作。
饿汉模式和懒汉模式对比
饿汉模式的优点就是简单但是它的缺点也比较明显。饿汉模式在程序运行主函数之前就会创建单例对象如果单例类的构造函数中所做的工作比较多就会导致程序迟迟无法进入主函数在外部看来就好像是程序卡住了。此外如果有多个单例类需要创建单例对象并且它们之间的初始化存在某种依赖关系比如单例对象A的创建必须在单例对象B之后此时饿汉模式也会存在问题因为我们无法保证这多个单例对象中的哪个对象先创建。而懒汉模式就能很好的解决上述饿汉模式的缺点因为懒汉模式并不是一开始就完成单例对象的创建因此不会导致程序迟迟无法进入主函数并且懒汉模式中各个单例对象创建的顺序是由各个单例类中的GetInstance函数第一次被调用的顺序决定因此是可控制的。懒汉模式的缺点就是在编码上比饿汉模式复杂在创建单例对象时需要考虑线程安全的问题。
其他版本的懒汉
懒汉模式还有一种比较经典的实现方式
将构造函数设置为私有并将拷贝构造函数和赋值运算符重载函数设置为私有或删除防止外部创建或拷贝对象。提供一个全局访问点获取单例对象。
class Singleton
{
public://2、提供一个全局访问点获取单例对象static Singleton* GetInstance(){static Singleton inst;return inst;}
private://1、将构造函数设置为私有并防拷贝Singleton(){}Singleton(const Singleton) delete;Singleton operator(const Singleton) delete;
};在单例类的GetInstance函数中定义一个静态的单例对象并返回。
由于实际只有第一次调用GetInstance函数时才会定义这个静态的单例对象这也就保证了全局只有这一个唯一实例。并且这里单例对象的定义过程是线程安全的因为现在的C标准保证多线程初始化static变量不会发生数据竞争可以视为原子操作。该方法属于懒汉模式因为局部静态变量不是在程序运行主函数之前初始化的而是在第一次调用GetInstance函数时初始化的。
这种版本的懒汉主要有如下两个缺点
单例对象定义在静态区因此太大的单例对象不适合使用这种方式。单例对象创建在静态区后没办法主动释放。
单例对象的释放
单例对象创建后一般在整个程序运行期间都可能会使用所以我们可以不考虑单例对象的释放程序正常结束时会自动将资源归还给操作系统。
如果要考虑单例对象的释放可以参考以下两种方式
1.在单例类中编写一个DelInstance函数在该函数中进行单例对象的释放动作当不再需要该单例对象时就可以主动调用DelInstance释放单例对象。
static void DelInstance()
{_mtx.lock();if (_inst ! nullptr){delete _inst;_inst nullptr;}_mtx.unlock();
}2.在单例类中实现一个内嵌的垃圾回收类在垃圾回收类的析构函数中完成单例对象的释放。在单例类中定义一个静态的垃圾回收类对象当该对象被消耗时就会调用其析构函数这时便对单例对象进行了释放。
//垃圾回收类
class CGarbo
{
public:~CGarbo(){if (_inst ! nullptr){delete _inst;_inst nullptr;}}
};