1.构造顺序
多重继承时,派生类构造时,基类构造调用的顺序与派生类声明的继承顺序相同,而与成员初始化顺序无关。而析构的调用顺序和构造函数的相反。
2.多重继承的二义性及解决方法
多重继承可能会产生大量的二义性,多个基类可能包含同名变量或函数。
解决方法:
2.1)基类名::数据成员名(成员函数(参数表))
明确指明要访问定义于哪个基类中的成员
#include<iostream>
using namespace std;
class Bed
{
protected:
int weight;
public:
Bed():weight(0)
{
cout << "Bed------------>construct" << endl;
}
void sleep() const
{
cout << "Sleeping..." << endl;
}
void setWeight(int i)
{
weight = i;
}
};
class Sofa
{
protected:
int weight;
public:
Sofa() : weight(0)
{
cout << "Sofa------------>construct" << endl;
}
void watchTV() const
{
cout << "Watching TV" << endl;
}
void setWeight(int i)
{
weight = i;
}
};
class SleeperSofa : public Bed, public Sofa
{
public:
SleeperSofa()
{
cout << "SleeperSofa------------>construct" << endl;
}
void foldOut()
{
cout << "Fold out the sofa" << endl;
}
void setWeight(int i)
{
Bed::setWeight(i);
Sofa::setWeight(i);
}
};
int main()
{
SleeperSofa ss;
ss.sleep();
ss.watchTV();
ss.foldOut();
ss.setWeight(34);
return 0;
}
如果把SleeperSofa的setWeight函数去除则会出现二义性
2.2)在数据成员或成员函数前加static
#include <iostream>
using namespace std;
class Furniture
{
protected:
int weight;
public:
Furniture(int v)
{
cout << "Furniture------------------->construct" << endl;
}
int setWeight(int w)
{
weight = w;
}
};
int Furniture::weight = 0;
class Bed : public Furniture
{
public:
Bed() : Furniture(10)
{
cout << "Bed------------------->construct" << endl;
}
void sleep() const
{
cout << "Sleeping..." << endl;
}
};
class Sofa : public Furniture
{
public:
Sofa() : Furniture(4)
{
cout << "Sofa------------------->construct" << endl;
}
void watchTV() const
{
cout << "Watching TV..." <<endl;
}
};
class SleeperSofa : public Bed, public Sofa
{
public:
SleeperSofa() : Sofa(), Bed()
{
cout << "SleeperSofa------------------->construct" << endl;
}
void foldOut() const
{
cout << "Fold out the sofa" << endl;
}
};
int main()
{
SleeperSofa ss;
ss.setWeight(32);
return 0;
}
运行上面的代码会发现出现出现二义性。主要原因是因为出现了两个setWeight函数产生的二义性。
如果在Furniture类的weight成员和stWeight函数成员的前面加上static修饰符的话,即可解决该二义性的产生。(因为static变量或函数在程序编译的时候已经分配了内存空间,只有一份拷贝)
2.3)虚拟继承
定义一个类A以虚拟继承的方式继承另一个类B,只需在继承方式前或后加上virtual修饰词即可。(虚拟继承主要解决多重继承时某个类别可能被重复继承的问题.虚拟继承主要解决多重继承时某个类别可能被重复继承的问题。)
所以也可以通过虚拟继承解决上面的问题。
class Sofa : public virtual Furniture
class Bed : public virtual Furniture
虚拟继承注意事项:
3.1)整个继承结构中,
直接或者间接继承虚基类的所有派生类中都必须在构造函数的成员初始化列表中列出对
虚基类的初始化(除虚基类的默认构造方法外)
3.2)虚基类的构造函数只会调用一次, 即
被最底层的派生类的构造函数调用,而最底派生类的其他基类对虚基类的构造函数的调用被自动忽略.
#include<iostream>
using namespace std;
//虚拟继承
class Furniture
{
public:
int v;
protected:
int weight;
public:
Furniture(int a)
{
cout << "Furniture------>construct" << endl;
}
void setWeight(int i)
{
weight =i;
cout << __LINE__ << endl;
}
int getWeight()const
{
return weight;
}
};
class Bed :public virtual Furniture
{
public:
Bed(int k)
: Furniture(k)
{
cout << "Bed------>construct" << endl;
}
void sleep()const
{
cout <<"Sleeping...\n";
}
void setWeight(int i)
{
cout << __LINE__ << endl;
}
};
class Sofa : virtual protected Furniture
{
public:
Sofa()
: Furniture(2)
{
cout << "Sofa------>construct" << endl;
}
void watchTV()const
{
cout <<"Watching TV.\n";
}
// void setWeight(int i)
// {
// cout << __LINE__ << endl;
// }
};
class SleeperSofa : public Sofa, public Bed
{
public:
SleeperSofa() :Sofa(),Bed(23),Furniture(3)
{
cout << "SleeperSofa------>construct" << endl;
}
void foldOut()const
{
cout <<"Fold out the sofa.\n";
}
};
int main()
{
SleeperSofa ss;
ss.setWeight(20);
ss.v;
cout<<ss.getWeight()<<endl;
}
特别注意:
如果A类和B类有相同的函数名且它们的参数类型、参数个数或者是返回值不一样,它们还是会产生二义性问题!!!(假设C类继承A,B)
分析如下:
名字查找过程
查找过程是一个域一个域的查找,直到全局域。每个类都有一个类域。看如下示例代码:
class ZooAnimal
{
public:
void print() const;
string is_a;
int ival;
priavte:
double dval;
};
class Bear : public ZooAniaml
{
public:
void print() const;
int mumble(int);
string name;
int ival;
};
Bear bear;
bear.is.a;
如上调用,is_a的名字解析过程为:
1.首先查找Bear类域,没有找到
2.查找Bear父类ZooAnimal类域,找到其声明,名字解析成功。
bear.ival;
这个调用解析过程如何呢?因为Bear和其父类ZooAnimal里都有该成员定义。答案是按照上面的顺序查找,查到后马上返回。
1.Bear类域中查找,找到ival声明,解析成功,不在向下查找。那么父类中名字就被子类中定义屏蔽了。
需要用bear.ZooAnimal::的形式来域限定符访问ZooAnimal类中成员ival,这样编译器直接在ZooAnimal类域中开始查找名字。
看如下调用各名字的位置:
int ival;
int Bear::mumble(int ival)
{
return ival + ::ival + ZooAnimal::ival + Bear::ival; // ival为形参,::ival为全局变量。
}
//而如下名字解析会出错:
int dval;
int Bear::mumble(int ival)
{
return ival + dval; // dval为ZooAnimal私有成员,错误
}
解析过程如下:
1.在Bear类域中查找,没有找到
2.在ZooAnimal类域中查找,找到,但是因为私有成员不能访问,编译错误,不在向下查找。
3.因为上一步,全局域中的名字就被屏蔽了。
多继承中的情况
多继承中,如果两个基类都存在同名的成员,那么子类对它的访问就是有二义性错误的。名字解析过程中(对父类域的查找是同时进行的),同时找到两个满足条件的成员,这让编译器无法确定应该使用哪个,所以产生编译错误。(
如何证明?)
#include<iostream>
using namespace std;
class Bed
{
protected:
int weight;
public:
Bed():weight(0)
{
cout << "Bed------->construct" << endl;
}
void sleep()const
{
cout <<"Sleeping...\n";
}
int setWeight(int i)
{
weight =i;
return i;
}
int a;
};
class Sofa
{
protected:
int weight;
public:
Sofa():weight(0)
{
cout << "Sofa------->construct" << endl;
}
void watchTV()const
{
cout <<"Watching TV.\n";
}
void setWeight()
{
// weight =i;
}
float a;
};
class SleeperSofa : public Bed, public Sofa
{
public:
SleeperSofa()
{
cout << "SleeperSofa------->construct" << endl;
}
void foldOut()const
{
cout <<"Fold out the sofa.\n";
}
};
int main()
{
SleeperSofa ss;
ss.watchTV();
ss.foldOut();
ss.sleep();
ss.a = 1;
ss.setWeight(1);
}