标签:
最开始是从构造函数开始着手(先声明这种方法不能定义一个不能被继承的类,这是一种错误的方法,但是很容易往这方面想),假设存在下面的继承体系:
现在假设B是一个不能被继承的类,那么如果存在B的子类C,那么C的构造过程应该会报错,那么如何能够让B能正常构造而C不能正常构造呢?首先A,B,C的构造函数和析构函数都假设是public的,最开始想的是让B私有继承自A,根据private继承的特性,父类中public和protected的成员在子类中都会变成private的,那么A的构造函数在B中就变成private的了,然后C在继承自B时是无法访问B中的private成员的,这样C就无法调用A的构造函数了。。。。开始这样想的,但是这种想法存在一个很大的问题。就是如果是普通的函数,前面的想法是正确的,但是对于构造函数而言就不能这么思考,对于上面的继承体系。C的构造过程是这样的:
所以上面那种处理方法是不能让B成为不能被继承的类的,子类C仍然可以正常构造!!!
如果把上面B私有继承替换成虚拟的私有继承呢?
此时看看构造C的过程:
从上面的分析可以看出使不使用虚基类效果是一样的。
#include <iostream> #include <string> using namespace std; class A { public: int a; A() { cout<<"a"<<endl; } }; //这里使不使用虚基类效果是一样的 class B:private virtual A { }; class C:public B { }; int main() { A a; B b; C c; cout<<"success"<<endl; return 0; }
上面的方法虽然是错误的,但是它也提供了一种思路,只是有些地方没有处理好。定义不能被继承的累关键仍然是要从构造函数着手。
在C++中要定义一个不能被继承的类,可以这么思考,如果存在子类,那么子类会调用父类的构造函数,那么我们可以将这个类的构造函数和析构函数都声明是私有的,那么这样它的子类构造时就会报错,这样这个类就是不能被继承的了。如果我们要获取这个类的对象,可以通过一个静态的方法来获取,这个静态 方法可以获取这个类的对象,也可以获取这个类的对象的指针,对应于在栈上还是在堆上分配内存。
先看在堆上分配内存的情况:
#include <iostream> using namespace std; class A { //构造函数和7构函数都被声明是私有的,这样就不能被继承了 private: A() { cout<<"a con"<<endl; } ~A() { cout<<"a des"<<endl; } //提供两个公有的方法来获取和释放A类型的对象 public: static A* getA() { A *a=new A(); return a; } static void deleteA(A* a) { delete a; } }; class B:public A { }; int main() { A* a=A::getA(); A::deleteA(a); // B b; return 0; }上面的代码能正常运行:但是注意这种方法构造的A对象都位于堆内存中,并且一定要注意通过定义的A::getA()来获取对象,A::delete(a)来释放对象,不能通过delete a来释放对象,因为此时析构函数是私有的了。
可以发现A的构造函数和析构函数都正常执行了,如果把main函数中注释的哪行代码注释掉,结果就会报错:一直提示A的构造函数是私有的。
上面这种方式已经可以定义一个不能被继承的类,但是对象A始终存在堆内存中,于是我想能不能尝试在栈内存中构造A的对象?
在栈上构造对象理论上讲是只需要在静态函数getA中返回一个对象A就可以了,不需要返回一个指向A的指针,如下。但是这里构造A是没问题了,可是析构A时出问题了,因为在getA返回对象a时存在一个临时对象,这个临时对象需要析构,然后main函数结束时也有一个对象需要析构,所以析构时会提示错误。
#include <iostream> using namespace std; class A { private: A() { cout<<"a con"<<endl; } //public: ~A() { cout<<"a desc"<<endl; } //提供两个公有的方法来获取和释放A类型的对象 public: static A getA() { A a; return a; } }; class B:public A { }; int main() { A a=A::getA(); B b; return 0; }
这个错误提示和上面的分析是一样的,即在调用A的析构函数时出错,如果把上面的析构函数声明是public(即把上面的析构函数上面的对public的注释去掉),构造函数声明是private的,那么就能正常构造A对象了。并且不能定义A的子类了。但是强烈不建议这么这种做法,因为一般构造函数和析构函数的访问修饰符是一样的!!!!
上面的解法总有一种怪怪的感觉,其实在第一错误的解法上稍微做一点改进就有一种很漂亮的解法了。
仍然是上面那个继承体系:
和第一种错误的解法相比,主要使将A的构造函数和析构函数声明是private的了,并且将B声明是A的友元类,其实这种解法和A的思路是一样的,就是让B能调用A的构造函数,让B的子类不能调用A的构造函数,只是第一种错误的解法没有满足这个要求。
更进一步,可以将它写成模板:
#include <iostream> using namespace std; template<class T> class A { friend T;//注意这里不需要class关键字 //将构造函数和7构函数都声明是私有的 private : A() { } ~A() { } }; class B:public virtual A<B> //这里一定需要使用虚继承,只有使用虚继承,它的子类才会直接调用A类的构造函数,这样才会报错,如果是普通继承,那么通过B类调用A类的构造函数时时不会报错的 { }; class C:public B { }; int main() { B b; cout<<"success"<<endl; // C c; return 0; }这就是最终的一种很好的写法了。
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/u012501459/article/details/48129327