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支队精髓气候的基类名起作用。

相关文章
|
6天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
29 4
|
7天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
26 4
|
30天前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
27 4
|
30天前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
21 1
|
30天前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
23 4
|
1月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
1月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
1月前
|
编译器 C语言 C++
C++入门3——类与对象2-2(类的6个默认成员函数)
C++入门3——类与对象2-2(类的6个默认成员函数)
23 3
|
1月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
53 1
|
1月前
|
编译器 C语言 C++
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
19 1