个旧市建设网站,上海 网站建设 外包,wordpress自定义密码,网络舆情监测工作1、请设计一个类#xff0c;不能被拷贝
拷贝只会出现在两个场景中#xff1a;拷贝构造函数以及赋值运算符重载#xff0c;因此想要让一个类禁止拷贝#xff0c;只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。在C98和C11都有相对应的方法来解决此问题#xff0c…1、请设计一个类不能被拷贝
拷贝只会出现在两个场景中拷贝构造函数以及赋值运算符重载因此想要让一个类禁止拷贝只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。在C98和C11都有相对应的方法来解决此问题下面我们分别讨论。
C98将拷贝构造函数与赋值运算符重载只声明不定义并且将其访问权限设置为私有即可。示例如下
class CopyBan
{
public://……
private://只声明不定义CopyBan(const CopyBan);//拷贝构造CopyBan operator(const CopyBan);//赋值运算符重载
};原因
设置成私有如果只声明没有设置成private用户自己如果在类外定义了就不能禁止拷贝了。只声明不定义不定义是因为该函数根本不会调用定义了其实也没有什么意义不写反而还简单而且如果定义了就不会防止成员函数内部拷贝了。
C11C11拓展delete的用法delete除了释放new申请的资源外如果在默认成员函数后加上delete即表示让编译器删除掉该默认成员函数。
class CopyBan
{CopyBan(const CopyBan) delete;//删除拷贝构造CopyBan operator(const CopyBan) delete;//删除赋值运算符重载
};库里面如下都是经典的防拷贝
unique_ptr。thread线程 。mutex锁 。istream 。ostream。
2、请设计一个类不能被继承
C98将该类的构造函数私有化即可因为子类的构造函数被调用时必须调用父类的构造函数初始化父类的那一部分成员但无论是何种继承方式父类的私有成员在子类是不可见的所以创建子类对象时子类就无法调用父类的构造函数对父类的成员初始化继而该类无法被继承。
class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit()//私有化构造函数{}
};C98的这种方式其实不够彻底因为这个类仍然可以被继承编译器不会报错只不过被继承后无法实例化出对象而已。因此我们推出C11的方法。
C11使用final关键字final修饰类表示该类不能被继承。此时就算继承后没有创建对象也会编译出错。
class A final
{// ....
};C98是委婉的不能让你继承C11是直接的不能让你继承。
3、请设计一个类只能在堆上创建对象
像我们平时创建对象常见有如下三种在不同区域创建对象的方式
class HeapOnly
{//……
};
int main()
{HeapOnly h1;static HeapOnly h2;HeapOnly* h3 new HeapOnly;return 0;
}既然只能在堆上创建对象也就是只能通过new操作创建对象有如下两种方式。
法一
将类的构造函数私有拷贝构造声明成私有防止别人调用拷贝在栈上生成对象。提供一个静态的成员函数在该静态成员函数中完成对象的创建。
class HeapOnly
{
public://静态成员函数完成对象的创建static HeapOnly* CreateObj(){return new HeapOnly;}
private://构造函数私有HeapOnly(){}//防拷贝//C98只声明不实现且声明成私有HeapOnly(const HeapOnly);//C11HeapOnly(const HeapOnly) delete;
};
int main()
{HeapOnly* ph1 HeapOnly::CreateObj();HeapOnly* ph2 HeapOnly::CreateObj();delete ph1;delete ph2;//HeapOnly h1;//栈-错误//static HeapOnly h2;//静态区-错误//HeapOnly copy(*ph2);//调用拷贝生成对象在栈区错误return 0;
}注意
我们没有必要对赋值运算符重载设置为私有只声明不实现或者加上delete禁掉因为赋值运算符重载是两个已经存在的对象既然已经存在那势必这俩对象就已经在堆区创建好了所以它们之间进行赋值操作并不会出错。除非你不想用那你可以把赋值运算符重载给禁掉。而拷贝构造是拿一个已经存在的对象去构造一个对象此对象是先前未存在的且拷贝构造后是在栈上的自然不符合题意因此需要把拷贝构造给禁掉而赋值运算符重载不需要禁掉。
法二
将析构函数私有化。
class HeapOnly
{
public:
private://析构函数私有化~HeapOnly(){}
};
int main()
{//HeapOnly ph1;err//static HeapOnly ph2;errHeapOnly* ph3 new HeapOnly;return 0;
}为何析构函数私有化就能确保只能在堆上创建对象呢
C是一个静态绑定的语言。在编译过程中所有的非虚函数调用都必须分析完成。即使是虚函数也需检查可访问性。因此 当在栈上生成对象时对象会自动调用析构函数释放对象也就说析构函数必须可以访问 否则编译出错。而在堆上生成对象由于析构函数由程序员调用(通过使用delete)所以不一定需要析构函数。
既然析构函数私有化如何delete你new出的资源呢
因为delete操作会调用析构函数而析构函数已经被置为私有了那就无法调用为了解决此问题我们只需要在类的内部提供一个静态成员函数既然你类外不能调用私用成员但是类里是可以调用的因此我们在此成员函数中调用析构函数完成delete操作。
class HeapOnly
{
public://静态成员函数释放new的对象static void DelObj(HeapOnly* ptr){delete ptr;}
private://析构函数私有化~HeapOnly(){}
};
int main()
{HeapOnly* ph3 new HeapOnly;//释放ph3HeapOnly::DelObj(ph3);return 0;
}当然我也可以使用delete this来释放new出的资源
class HeapOnly
{
public:void DelObj(){delete this;}
private://析构函数私有化~HeapOnly(){}
};
int main()
{HeapOnly* ph3 new HeapOnly;//释放ph3ph3-DelObj();return 0;
}delete this–对象请求自杀执行后不能再访问this指针。换句话说你不能去检查它、将它和其他指针比较、和 NULL比较、打印它、转换它以及其它的任何事情。不是很推荐这种方式。
4、请设计一个类只能在栈上创建对象
法一
将构造函数设为私有防止外部直接调用构造函数在堆上创建对象。提供静态成员函数内部调用私有的构造函数完成对象的创建。
class StackOnly
{
public://静态成员函数内部调用构造函数创建对象static StackOnly CreateObj(){return StackOnly();//传值返回 —— 拷贝构造}
private://构造函数私有StackOnly(){}
};
int main()
{StackOnly h1 StackOnly::CreateObj();//static StackOnly h2; 错误//StackOnly* h3 new StackOnly; 错误return 0;
}此法有一缺陷无法避免外部调用拷贝构造函数在静态区、堆区……创建对象。
int main()
{StackOnly h1 StackOnly::CreateObj();//栈区static StackOnly h2(h1);//调用拷贝构造在静态区创建对象StackOnly* h3 new StackOnly(h1);//调用拷贝构造在堆区创建对象return 0;
}法二
把构造函数设为公有。屏蔽operator new函数和operator delete函数。
class StackOnly
{
public:StackOnly(){}
private://C98void* operator new(size_t size);void operator delete(void* p);//C11//void* operator new(size_t size) delete;//void operator delete(void* p) delete;
};
int main()
{StackOnly h1;//StackOnly* h3 new StackOnly(h1);不能使用new在堆区创建对象return 0;
}new和delete默认调用的是全局的operator new函数和operator delete函数但如果一个类重载了专属的operator new函数和operator delete函数那么new和delete就会调用这个专属的函数。所以只要把operator new函数和operator delete函数屏蔽掉那么就无法再使用new在堆上创建对象了。
上述做法虽然成功避免了在堆区创建对象但是无法避免在静态区或全局创建对象。
class StackOnly
{
private:void* operator new(size_t size) delete;void operator delete(void* p) delete;
};
StackOnly h1;//全局区
int main()
{static StackOnly h2;//静态区return 0;
}综上其实无论是法一还是法二多少都会存在点瑕疵总是会有老六的出现只能说是不那么严谨的情况下来看法一算是ok的。
5、请设计一个类只能创建一个对象单例模式
设计模式
设计模式Design Pattern是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期七国之间经常打仗就发现打仗也是有套路的后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。使用设计模式的目的为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化设计模式是软件工程的基石脉络如同大厦的结构一样。
现在已经总结出了23种设计模式
创建型模式共五种工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。结构型模式共七种适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。行为型模式共十一种策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其中用的最多的就是单例模式。下面来展开讨论。
单例模式一个类只能创建一个对象即单例模式该模式可以保证系统中该类只有一个实例并提供一个访问它的全局访问点该实例被所有程序模块共享。比如在某个服务器程序中该服务器的配置信息存放在一个文件中这些配置数据由一个单例对象统一读取然后服务进程中的其他对象再通过这个单例对象获取这些配置信息这种方式简化了在复杂环境下的配置管理。
单例模式有两种实现模式饿汉模式和懒汉模式。下面展开讨论。
饿汉模式
饿汉模式就是不管你将来用不用程序启动时就创建一个唯一的实例对象。
实现方式如下
将构造函数私有化并将拷贝构造和拷贝赋值设为私有或删除防止外部随意创建对象或拷贝。在类里创建一个static静态对象的指针在进入程序入口之前就完成单例对象的初始化。提供一个static静态成员函数用来获取单例对象的指针。将拷贝构造和拷贝赋值私有化防止类外调用拷贝构造创建对象。
class Singleton
{
public://静态成员函数内部获取单例对象的指针static Singleton* GetInstance(){return _spInst;}void print();
private:Singleton(){}//C98 防拷贝Singleton(const Singleton);Singleton operator(Singleton const);//C11 防拷贝//Singleton(const Singleton) delete;//Singleton operator(Singleton const) delete;static Singleton* _spInst;//声明int _a 0;
};
Singleton* Singleton::_spInst new Singleton;//定义在程序入口之前就完成单例对象的初始化
void Singleton::print()
{cout _a endl;
}
int main()
{Singleton::GetInstance()-print();//Singleton st1;err//Singleton* st2 new Singleton;err//Singleton copy(*Singleton::GetInstance());errreturn 0;
}再比如我现在有一个信息管理的类需要保证进程里只有一份这样的信息那么就需要把它设定为单例整体框架和上面差不多其实具体实现细节有所变动罢了
//InfoMgr —— 单例
class InfoMgr
{
public://静态成员函数获取单例对象指针static InfoMgr* GetInstacne(){return _spInst;}//修改信息void SetAddress(const string s){_address s;}//获取信息string GetAddress(){return _address;}
private://构造函数私有化InfoMgr() {}//删除拷贝构造InfoMgr(const InfoMgr) delete;string _address;int _secretKey;static InfoMgr* _spInst;//声明
};
InfoMgr* InfoMgr::_spInst new InfoMgr;//定义
int main()
{//全局只有一个InfoMgr对象InfoMgr::GetInstacne()-SetAddress(江苏省南京市);cout InfoMgr::GetInstacne()-GetAddress() endl;//江苏省南京市return 0;
}如果这个单例对象在多线程高并发环境下频繁使用性能要求较高那么显然使用饿汉模式来避免资源竞争提高响应速度更好。
懒汉模式
如果单例对象构造十分耗时或者占用很多资源比如加载插件啊 初始化网络连接啊读取文件啊等等而有可能该对象程序运行时不会用到那么也要在程序一开始就进行初始化就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式延迟加载更好。
还是以上述信息管理的类为例懒汉模式的实现方式如下
将构造函数置为私有并将拷贝构造函数和赋值运算符重载函数设为私有或删除防止外部创建或拷贝对象。提供一个指向单例对象的static指针并在程序入口之前先将其初始化为空。提供一个static静态成员函数只有当static指针为空时才初始化也就是第一次调用此成员函数才创建对象最后返回单例对象的指针。将拷贝构造和拷贝赋值私有化防止类外调用拷贝构造创建对象。
//懒汉 -- 一开始不创建对象第一次调用GetInstacne再创建对象
class InfoMgr
{
public://静态成员函数获取单例对象指针static InfoMgr* GetInstacne(){if (_spInst nullptr){_spInst new InfoMgr;}return _spInst;}//修改信息void SetAddress(const string s){_address s;}//获取信息string GetAddress(){return _address;}
private://构造函数私有化InfoMgr(){}//删除拷贝构造InfoMgr(const InfoMgr) delete;string _address;int _secretKey;static InfoMgr* _spInst;//声明
};
InfoMgr* InfoMgr::_spInst nullptr;//定义
int main()
{//全局只有一个InfoMgr对象InfoMgr::GetInstacne()-SetAddress(江苏省南京市);cout InfoMgr::GetInstacne()-GetAddress() endl;//江苏省南京市return 0;
}懒汉模式这样写是有问题的还需要加锁双检查加锁饿汉模式不需要加锁。