二义性和支配规则
上面的程序我们在构建不同类的成员的时候都是不同的声明,但是我们在解决某些问题的时候,可能会出现多个基类的成员函数相同,那么我们在访问的时候,会不会出现不确定的情况?这就是我们今天学的二义性
我们先来看一个程序:
#include<iostream> using namespace std; class Baseclass1 { public: void seta(int x) { a = x; } void show() { cout << "a=" << a << endl; } private: int a; }; class Baseclass2 { public: void setb(int x) { b = x; } void show() { cout << "b=" << b << endl; } private: int b; }; class Derivedclass :public Baseclass1, public Baseclass2 { }; int main(void) { Derivedclass obj; obj.seta(2); obj.show();//出现二义性,不能编译; obj.setb(4); obj.show();//出现二义性,不能编译; }
上面的程序在编译的时候会报错,由于我们没指名调用的函数是呢哪一个类的成员,所以编译器无法识别,那我们如何来解决这种情况喃?
若要消除二义性,需要使用作用域运算符::;
我们只需要将上面的程序更改如下就可以避免二义性; obj.Baseclass1::show(); obj.Baseclass2::show(); }
我们除了这一种解决情况,。是不是就没有其他的解决方案?
我们也可以通过在派生类Derivedclass总定义下列函数show(),然后去掉主函数中的第一个obj.show()语句;
#include<iostream> using namespace std; class Baseclass1 { public: void seta(int x) { a = x; } void show() { cout << "a=" << a << endl; } private: int a; }; class Baseclass2 { public: void setb(int x) { b = x; } void show() { cout << "b=" << b << endl; } private: int b; }; class Derivedclass :public Baseclass1, public Baseclass2 { public: void show() { Baseclass1::show(); Baseclass2::show(); } }; int main(void) { Derivedclass obj; obj.seta(2); obj.setb(4); obj.Derivedclass::show(); }
最终得到的结果和上面的程序结果一样。
支配规则
类X中的名字N支配类Y中的同名的名字N,是指类X一类Y作为它的一个基类。
访问共同基类的成员时可能出现二义性
如果一个派生类从多个基类派生而来,而这些基类又有一个共同的基类,则在这个派生类中访问这个共同基类中的成员时可能产生二义性.
//#include<iostream> //using namespace std; //class Baseclass1 //{ //public: // void seta(int x) { a = x; } // void show() { cout << "a=" << a << endl; } //private: // int a; //}; //class Baseclass2 //{ //public: // void setb(int x) { b = x; } // void show() { cout << "b=" << b << endl; } //private: // int b; //}; //class Derivedclass :public Baseclass1, public Baseclass2 //{ //public: // void show() // { // Baseclass1::show(); // Baseclass2::show(); // } //}; //int main(void) //{ // Derivedclass obj; // obj.seta(2); // obj.setb(4); // obj.Derivedclass::show(); //} #include<iostream> using namespace std; class Base { protected: int val; }; class Baseclass1 :public Base { public: void seta(int x) { val = x; } }; class Baseclass2 :public Base { public: void setb(int x) { val = x; } }; class Derivedclass :public Baseclass1, public Baseclass2 { public: void show() { cout << "val=" << val << endl; } //h含义不清,不能编译;} }; int main(void) { Derivedclass obj; obj.seta(2); obj.show(); obj.setb(4); obj.show(); } 程序分析:该程序中四个类的关系如下图,由于两条继承路径上的成员val 相互之间没有支配关系。
若要消除二义性,我们依旧要使用作用域运算符;
程序更改为:
//#include<iostream> //using namespace std; //class Baseclass1 //{ //public: // void seta(int x) { a = x; } // void show() { cout << "a=" << a << endl; } //private: // int a; //}; //class Baseclass2 //{ //public: // void setb(int x) { b = x; } // void show() { cout << "b=" << b << endl; } //private: // int b; //}; //class Derivedclass :public Baseclass1, public Baseclass2 //{ //public: // void show() // { // Baseclass1::show(); // Baseclass2::show(); // } //}; //int main(void) //{ // Derivedclass obj; // obj.seta(2); // obj.setb(4); // obj.Derivedclass::show(); //} #include<iostream> using namespace std; class Base { protected: int val; }; class Baseclass1 :public Base { public: void seta(int x) { val = x; } }; class Baseclass2 :public Base { public: void setb(int x) { val = x; } }; class Derivedclass :public Baseclass1, public Baseclass2 { public: void show() { cout << "val=" << Baseclass1::val<< endl; } //h含义不清,不能编译;} //void show() { cout << "val=" << Baseclass2::val << endl; } }; int main(void) { Derivedclass obj; obj.seta(2); obj.show(); obj.setb(4); obj.show(); } 注意:void show(){cout<<"val="<<Base::val<<endl;也有二义性;
示例类Derivedclass的对象包含基类Base的两个基类子对象:
#include<iostream> using namespace std; class Base { protected: int val; }; class Baseclass1 :public Base { public: void seta(int x) { val = x; } }; class Baseclass2 :public Base { public: void setb(int x) { val = x; } }; class Deviredclass :public Baseclass1, public Baseclass2 { public: void show(); }; void Deviredclass::show() { cout << "Baseclass val=" << Baseclass1::val << endl; cout << "Baseclass2 val=" << Baseclass2::val << endl; } int main(void) { Deviredclass obj; obj.seta(3); obj.setb(4); obj.show(); }
通过上述例子我们不难看出,对于基类Base,在访问Base的成员时,发生了冲突,这个时候就要用到作用域运算符。如上图。
虚基类
引进虚基类的目的是为了解决二义性问题,使得公共基类在他的派生类对象中只产生一个基类子对象。
虚基类说明格式如下:
virtual<继承方式>基类名
virtual是说明虚基类的关键字。虚基类的说明是在派生类名的后面。在派生类中使用关键字virtual会导致他们共享基类Base的同一个单独公共对象。因此,Base是虚基类。
代码演示:
#include<iostream> using namespace std; class Baseclass1 { public: void seta(int x) { a = x; } void show() { cout << "a=" << a << endl; } private: int a; }; class Baseclass2 { public: void setb(int x) { b = x; } void show() { cout << "b=" << b << endl; } private: int b; }; class Derivedclass :public Baseclass1, public Baseclass2 { public: void show() { Baseclass1::show(); Baseclass2::show(); } }; int main(void) { Derivedclass obj; obj.seta(2); obj.setb(4); obj.Derivedclass::show(); } //#include<iostream> //using namespace std; //class Base //{ //protected: // int val; //}; //class Baseclass1 :public Base //{ //public: // void seta(int x) { val = x; } //}; //class Baseclass2 :public Base //{ //public: // void setb(int x) { val = x; } //}; //class Derivedclass :public Baseclass1, public Baseclass2 //{ //public: // void show() { cout << "val=" << Baseclass1::val<< endl; } //h含义不清,不能编译;} // //void show() { cout << "val=" << Baseclass2::val << endl; } //}; //int main(void) //{ // Derivedclass obj; // obj.seta(2); // obj.show(); // obj.setb(4); // obj.show(); //} // #include<iostream> using namespace std; class Base { protected: int val; }; class Baseclass1 :public virtual Base { public: void seta(int x) { val = x; } }; class Baseclass2 :public virtual Base { public: void setb(int x) { val = x; } }; class Deviredclass :public Baseclass1, public Baseclass2 { public: void show(); }; void Deviredclass::show() { cout << "Baseclass val=" << val << endl; } int main(void) { Deviredclass obj; obj.seta(3); obj.show(); obj.setb(4); obj.show(); }
注意:
一个派生类可以公有或私有继承一个或多个虚基类,关键字virtual和关键字public,private的位置无关紧要,但是要放在基类名之前。比呢且virtual支队精髓气候的基类名起作用。