目录
1.请设计一个类,不能被拷贝
2. 只能在堆上创建对象,不能在栈上或全局/静态区直接创建对象
2.1 方案1
2.2 方案二
3.只能在栈上创建对象
4.不能被继承
1.请设计一个类,不能被拷贝
拷贝只会放在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
C++98 可将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。
class CopyBan { // ... private: CopyBan(const CopyBan&); CopyBan& operator=(const CopyBan&); //... };原因:
- 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
- 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。
class CopyBan { // ... CopyBan(const CopyBan&)=delete; CopyBan& operator=(const CopyBan&)=delete; //... };2. 只能在堆上创建对象,不能在栈上或全局/静态区直接创建对象
只能在堆上创建对象,说明只能通过只指针的方式指向对象,在构造和析构函数上做文章。
2.1 方案1
析构函数私有化,然后需要提供 delete 的方法,有两种delete方式。
class HeapOnly { public: //不加static也行,必须实例化,p1->Destory1,p1不能为空 //加上static可以实例化调用,C++ 保证 delete nullptr 无操作 static void Destroy1(HeapOnly* ptr) { //合法:因为这是在 HeapOnly 的成员函数内部, //可以访问 HeapOnly 的私有成员函数和数据成员,调用 delete 来销毁对象。 delete ptr; } void Destroy2() { delete this; } private: ~HeapOnly() { cout << "~HeapOnly" << endl; } };这样就不能直接创建对象了,因为这样必须需要构造函数,而new 操作只负责“构造”,不负责“析构”。然后可以通过下面三种方式释放空间(只能使用一个)。类内的 this 就是 ptr ,就是指向类的指针。但静态成员函数中不能直接用 this 和 析构,因为 this 是一个具体的对象。
int main() { //HeapOnly hp1; //static HeapOnly hp2;//执行完都需要 自动 调用析构函数, 会出问题,不能在栈上或全局/静态区 直接创建对象 HeapOnly* hp3 = new HeapOnly; //执行delete 自定义类型,会调用析构函数,失败 //delete hp3;//不能直接释放,因为也要调用析构函数 //也不用再主动 delete 释放了,可以调用内部函数完成。 HeapOnly::Destroy1(hp3);//1. 通过静态成员函数来销毁对象,成功 hp3->Destroy1(hp3);//2. 通过对象来调用静态成员函数来销毁对象,成功 hp3->Destroy2();//3. 通过this指针来销毁对象,成功 return 0; }2.2 方案二
构造函数私有化,那就提供一个 static 构造的方法
这里必须加static,因为调用成员函数需要现有对象,而对象只能通过构造函数,但构造函数私有化了,先有鸡还是先有蛋问题。拷贝构造 和 operator= 也应该禁掉。
class HeapOnly { public: //不加static不行,因为调用成员函数需要现有对象, //而对象只能通过构造函数,但构造函数私有化了 static HeapOnly* CreateObj() { return new HeapOnly; //合法:因为这是在 HeapOnly 的成员函数内部, //可以访问 HeapOnly 的私有成员函数和数据成员,调用 new 来创建对象。 } // 禁止拷贝和移动构造(防止栈上通过已有对象创建新对象) HeapOnly(const HeapOnly&) = delete; HeapOnly(HeapOnly&&) = delete;//move()后可以调用 // 禁止拷贝和移动赋值(虽不创建新对象,但为逻辑完整) HeapOnly& operator=(const HeapOnly&) = delete; HeapOnly& operator=(HeapOnly&&) = delete; private: HeapOnly()//构造函数私有化,不能在栈上或全局/静态区 直接创建对象 { } }; int main() { HeapOnly* hp1 = HeapOnly::CreateObj(); //合法:静态函数才能成功 return 0; }3.只能在栈上创建对象
无法 new,所以必须构造函数私有,然后提供一个 static 的函数来构造。这里是无参的,当然也可以传递参数。拷贝构造函数不能禁掉,因为要传值返回,传不了指针,因为函数内部创建的是局部变量,如果是创建一个 static 对象,那么全局就只有一个 StaticOnly 指针了,也不行。
class StackOnly { public: static StackOnly CreateObj() { return StackOnly(); } //StackOnly(const StackOnly& st) = delete;//禁掉后无法 CreateObj 传值返回 void* operator new(size_t size) = delete;//相当于间接禁用new。或者设为私有 //当然我们也可以operator 重载new //void* operator new(size_t size) //{ // cout << "void * operator new(size_t size)" << endl; // return malloc(size); //} private: StackOnly() {} // 构造函数私有化,禁止在堆上创建对象 }; int main() { //因为new必须调用构造函数来创建对象,所以当构造函数私有化时,无法在堆上创建对象,只能在栈上创建对象。 StackOnly obj1 = StackOnly::CreateObj(); // 合法:通过静态成员函数创建对象 return 0; }new StackOnly(obj1) 执行两步:
- 调用 operator new 分配堆内存。operator new是全局的,当然这里重载自己的专属operator new,不调用全局的。
- 在分配的内存上调用拷贝构造函数,用 obj1 作为参数初始化这个新对象。因为拷贝构造函数是 public,所以这一步合法。而不使用()直接new 调用的才是构造函数,
StackOnly* obj2 = new StackOnly(obj1);4.不能被继承
C++98方式:
因为子类构造要调用父类的构造,同上将构造函数私有化,然后设计静态方法创建对象返回即可。
class NonInherit { public: static NonInherit GetInstance() { return NonInherit(); } private: NonInherit() {} };C++11方法:final关键字,final修饰类,表示该类不能被继承。
class A final { // .... };本篇结束!