item40:谨慎的使用多重继承
多重继承带来的符号的歧义性
#include <iostream>
#include <vector>
using namespace std;
class A {
public:
void f() { cout << "A" << endl; }
};
class B {
private:
void f();
};
class C : public A, public B {};
int main() {
C c;
c.A::f();
}
钻石形多重继承
on the one hand, it inherits a copy from each of its base classes, so that suggests that IOFile should have two fileName data members. On the other hand, simple logic says that an IOFile object has only one file name, so the fileName field it inherits through its two base classes should not be replicated.
virtual虚继承
解决了多重继承的问题
在标准库中,basic istream和basic ostream库都是虚继承自basic ios的类,而basic iostream又继承自basic istream和basic ostream。
virtual继承的成本
objects created from classes using virtual inheritance are generally larger than they whould be without virtual inheritance. Access to data members in virtual base classes is also slower than to those in non-virtual base classes. The details vary from compiler to compiler, but the basic thrust is clear: virtual inheritance costs.
It costs in other ways, too. The rules governing the initialization of virtual base classes are more complicated and less intuitive than are those for non-virtual bases.
虚基类的初始化问题
class A{
public:
A(A* a);
};
class B:virtual public A{
public:
B(B* a):A(a){};
};
class C{
public:
C(C* a):A(a){};
};
class D:public B, public C{
public:
D(D* a):B(a), C(a){}; // compile error
D(D* a):A(a), B(a), C(a){}; // correct
};
class E:public D{
public:
E(E* a):D(a){}; // compile error
E(E* a):A(a), E(a){}; // correct
};
虚继承成本高,要维护基类
使用多重继承
item41:了解隐式接口和编译期多态
显示接口和运行期多态
class Widget{
public:
virtual std::size_t size() const;
virtual void normalize();
}
void doProcessing(Widget& w){
if(w.size() > 10 && w != someWidget){
Widget temp(w);
temp.normalize();
temp.swap(w);
}
}
使用虚函数可以实现运行期的多态。
隐式接口和编译期多态
template<typename T>
void doProcessing(T& w){
if(w.size() > 10 && w != someWidget){
T temp(w);
temp.normalize();
temp.swap(w);
}
}
在编译期做类型推导,得到T的类型。实现编译期多态
不论T是什么类型,其中必须要有size、normalize、swap、!=等接口。
item42:理解typename的双重含义
template<class T> class Widget;
template<typename T> class Widget;
class不能替代typename,但是typename可以替代class
template<typename C>
void print2nd(const C &constainer){ // C被称为从属名称
if(constainer.size() > 2){
// 这一行将会报错!!!!原因在下面
C::const_iterator iter(constainer.cbegin()); // C::const_iterator被称为嵌套从属名称
++ iter;
std::cout << *iter << std::endl;
}
}
int main(){
std::vector<int> v{1,2,3,4};
print2nd(v);
return 0;
}
上面出错的语句原因不太直观,可以参考如下语句:
C::const_iterator * x;
编译器可能会以为在C中有const_iterator类型的成员,然后✖️x,解决方法为在嵌套从属名称前加typename:
typename C::const_iterator * x; // 告知编译器后面是一个类型
typename使用规则也有例外:
- 在以下这种情况下不需要加type name
template<typename T>
class Derived:public Base<T>::Nested{ // base class list:typename not allowed
public:
// base class indetifier in mem.init.list:typename not allowed
explicit Derived(int x):Base<T>::Nested(x){
// use of nested dependent type name:typename
typename Base<T>::Nested temp;
}
};
另外,还可以使用typedef或者using来简化编程:
template<typename IterT>
void workWithIterator(IterT iter){
typename std::iterator_traits<IterT>::value_type temp(*iter);
}
// use typedef
template<typename IterT>
void workWithIterator(IterT iter){
typedef typename std::iterator_traits<IterT>::value_type value_type;
value_type temp(*iter);
}