c++面向对象程序设计——基类和派生类(二)

简介: c++面向对象程序设计——基类和派生类(二)

二义性和支配规则


上面的程序我们在构建不同类的成员的时候都是不同的声明,但是我们在解决某些问题的时候,可能会出现多个基类的成员函数相同,那么我们在访问的时候,会不会出现不确定的情况?这就是我们今天学的二义性

我们先来看一个程序:


#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是虚基类。


cadb6b4693524c93dd782f0a2307f621_4b2efd2f24d449e0ade0ae3f0862757c.png


代码演示:


#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();
}


74778371004d265c7d2a3b348a366921_b7a1004112824e4995a239ef14575f53.png


注意:

一个派生类可以公有或私有继承一个或多个虚基类,关键字virtual和关键字public,private的位置无关紧要,但是要放在基类名之前。比呢且virtual支队精髓气候的基类名起作用。

相关文章
存储 编译器 Linux
13 0
|
1天前
|
编译器 C++
标准库中的string类(上)——“C++”
标准库中的string类(上)——“C++”
|
1天前
|
编译器 C++
自从学了C++之后,小雅兰就有对象了!!!(类与对象)(中)——“C++”
自从学了C++之后,小雅兰就有对象了!!!(类与对象)(中)——“C++”
|
1天前
|
存储 编译器 C++
自从学了C++之后,小雅兰就有对象了!!!(类与对象)(上)——“C++”
自从学了C++之后,小雅兰就有对象了!!!(类与对象)(上)——“C++”
|
2天前
|
C++
【C++成长记】C++入门 | 类和对象(下) |Static成员、 友元
【C++成长记】C++入门 | 类和对象(下) |Static成员、 友元
|
21天前
|
存储 C++ 容器
C++入门指南:string类文档详细解析(非常经典,建议收藏)
C++入门指南:string类文档详细解析(非常经典,建议收藏)
31 0
|
21天前
|
存储 编译器 C语言
C++入门: 类和对象笔记总结(上)
C++入门: 类和对象笔记总结(上)
30 0
|
2天前
|
存储 编译器 C++
【C++成长记】C++入门 | 类和对象(中) |拷贝构造函数、赋值运算符重载、const成员函数、 取地址及const取地址操作符重载
【C++成长记】C++入门 | 类和对象(中) |拷贝构造函数、赋值运算符重载、const成员函数、 取地址及const取地址操作符重载
|
11天前
|
存储 算法 C语言
【C++初阶】8. STL初阶 + String类
【C++初阶】8. STL初阶 + String类
47 1
|
11天前
|
C语言 C++
【C++初阶】9. string类的模拟实现
【C++初阶】9. string类的模拟实现
36 1