c++不像java一样有纯接口类的的语法,但我们可以通过一些手段实现相同的功能。
一:
考虑这样的代码:
class A
{
protected:
virtual ~A()
{
cout << __FUNCTION__ << endl;
}
};
class B : public A
{
public:
virtual ~B()
{
cout << __FUNCTION__ << endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A* p1 = new A; //[1]有问题
delete p1;
B* p2 = new B;
delete p2; //[2]没问题的
A* p3 = new B;
delete p3; //[3] 有问题
return 0;
}
通过在类中,将类的构造函数或者析构函数申明成protected ,可以有效防止类被实例话,要说实用的话,构造函数是protected更有用,肯定能保证类不会被实例化,而如果析构函数是protected的话,构造函数不是protected的话,还可能存在编译通过的漏洞,如下:
A:
class A
{
protected:
A()
{
cout << __FUNCTION__ << endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A* p1 = new A; //编译不通过,无法访问protected构造函数
delete p1;
return 0;
}
B:
class A
{
protected:
~A()
{
cout << __FUNCTION__ << endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A* p1 = new A; //编译通过,此时因为仅仅是用到了A的构造函数,还不需要它的析构函数
return 0;
}
C:
class A
{
protected:
~A()
{
cout << __FUNCTION__ << endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A* p1 = new A;
delete p1; //编译失败,因为编译器发现A的析构函数是protected
return 0;
}
所以,一种可行的办法貌似是这样的:
class A
{
protected:
virtual ~A()
{
cout << __FUNCTION__ << endl;
}
};
class B : public A
{
};
int _tmain(int argc, _TCHAR* argv[])
{
B* p =new B; //这种情况下确实是可行的
delete p;
return 0;
}
由于B public继承自A,所以其可以完全访问A的构造或析构函数,但是:
int _tmain(int argc, _TCHAR* argv[])
{
A* p =new B;
delete p; //由于p变成指向A的指针,字面上编译器需要知道A的析构函数,然后A的析构函数又是protected
return 0;
}
即便像这样B显示重载了A的析构函数:
class A
{
protected:
virtual ~A()
{
cout << __FUNCTION__ << endl;
}
};
class B : public A
{
public:
virtual ~B()
{
cout << __FUNCTION__ << endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A* p =new B;
delete p; //也还是不行,因为重载是运行时的事情,在编译时编译器就认定了A的析构函数,结果无法获取
return 0
}
小结:
貌似用protected这样的方法并不是很恰当,虽然在遵守一定规则的情况下确实有他的实用价值,但并不是很通用。
二:
其实上面protected的思路是对的,无非是让父类无法实例化,那么为了让父类无法实例化,其实还有一个方法,使用纯虚函数。
class A
{
public: //这里就不用protected了
virtual ~A() = 0;
};
class B : public A
{
};
int _tmain(int argc, _TCHAR* argv[])
{
B* p =new B;
delete p;
return 0;
}
这样写貌似不错,以往大家都把类中的一般成员函数写成纯虚的,这次将析构函数写成纯虚的,更加增加通用性,编译也通过了,但就是在链接的时候出问题,报错说找不到A的析构函数的实现,很显然吗,因为A的析构是纯虚的吗。
class A
{
public: //这里就不用protected了
virtual ~A() = 0 //它虽然是个纯虚函数,但是默认实现,可以有
{ //这个语法很好很强大,经过小强的指点终于高清了他的用法(完全是为了实现其接口类而弄的语法吧)
cout << __FUNCTION__ << endl;
}
};
class B : public A
{
};
int _tmain(int argc, _TCHAR* argv[])
{
B* p =new B;
delete p;
A* p2 =new B;
delete p2; //不用担心编译器报错了,因为此时A的析构函数是public
return 0;
}
如此终于大功告成了,注意,不能将构造函数替代上面的析构函数的用法,因为构造函数是不允许作为虚函数的。
补充:以上那个语法就真的只是为了这种情况而存在的,因为一般我们在虚类中申明的接口:
virtual foo() = 0;
virtual foo() = 0 {}
这两种写法是完全没有区别的,纯虚函数的默认实现,仅仅在它是析构函数中才有意义!!!
所以可以说,老外是完全为了这一个目的而发明了这种语法...
最终的接口类
class Interface
{
public:
virtual ~Interface() = 0 {}
};
应该挺完美的了吧,hiahia