C++ オブジェクト指向プログラミング
参考书目:《C++面向对象程序设计》—— 谭浩强 《C++程序设计:思想与方法》—— 翁惠玉
第 5 章: 継承と派生
1. 継承と派生の概念
継承は普遍的に重要な自然の性質です。派生により、新しいクラスは共通の機能を継承しながら、より多彩な個性を持つことができます。オブジェクト指向プログラミングでは、継承は C++ でソフトウェアの再利用を実現する主な手段です。一般に、階層的な分類方法は、物事間の関係を説明するために使用されます。
クラスの階層関係の概要:
-
クラス階層図において、下位クラスは上位クラスの特別なクラスです。
-
下位クラスは自動的に上位クラスの特性を持ち、また独自の新しい特性も持ちます。
-
階層図を下に進むほど、その特性はより具体的になります。
-
このトップダウンの階層関係は、継承と派生のプロセスを反映しています。
-
C++ オブジェクト指向テクノロジでも、この継承メカニズムが使用されます。
注:1.由基类派生出新类的过程称为派生(派生类自身也可以作为基类派生出新的派生类)。 2.继承是指派生类自动拥有基类的属性和行为特征(派生类自动拥有基类的属性和行为,并表现出自身新的属性和行为特征 )。 类的继承和派生机制使程序员无须修改已有的类,只需在既有类的基础上,根据问题的实际需要,通过增加部分代码或修改少量代码而得到新的类(派生类),从而很好的解决了程序代码的重用问题。
C++ の継承メカニズム:
いわゆる継承とは、既存のクラス A に基づいて新しいクラス B を作成することです。クラス A は基本クラスまたは親クラスと呼ばれ、クラス B は派生クラスまたはサブクラスと呼ばれます。サブクラスはその親クラスから既存の特性を取得します。これはクラス継承と呼ばれる現象です。別の観点から見ると、親クラスからサブクラスを生成することをクラス派生と呼びます。
基底クラスは複数の派生クラスを派生でき、各派生クラスを基底クラスとして使用して新しい派生クラスを派生できます。1 つの基本クラスからのみ派生する派生クラスは、単一継承と呼ばれます。
具体的なプロセスを図に示します。
派生クラスは複数の基本クラスから派生することもできます。つまり、派生クラスは 2 つ以上の基本クラスを持つことができます。2 つ以上の基本クラスを持つ派生クラスは多重継承と呼ばれます。
具体的なプロセスを図に示します。
2. 派生クラスの宣言方法
派生クラス宣言の形式: クラス派生クラス名: [継承メソッド] 基本クラス名 {派生クラスの新しいメンバー宣言};
継承メソッドには、public、private、protected が含まれます。省略した場合、システムはデフォルトでプライベートになります。
例: 基本クラス Student が宣言されており、それに基づいて単一継承を通じて派生クラス Student1 が作成されたとします。
class Student
{
private :
int num;
string name;
char sex;
public:
void display( )
{
cout<<"num: "<<num<<endl;
cout<<"name: "<<name<<endl;
cout<<"sex: "<<sex<<endl;
}
};
class Student1: public Student
{
private:
int age;
string addr;
public:
void display_1()
{
cout <<"age: "<<age<<endl;
cout <<"address: "<<addr<<endl;
}
};
3. 派生クラスの構成
派生クラスのメンバーには、基本クラスから継承したメンバーと自分で追加したメンバーが含まれます。継承された基本クラスのメンバーは同じ基本クラスの派生クラスの共通の機能を反映し、新しく追加されたメンバーは派生クラスの個性を反映します。
派生クラスを構築する作業のいくつかの部分:
- 基本クラスからメンバーを受け取ります。派生クラスは、コンストラクターとデストラクターを除く基本クラスのすべてのメンバーを受け取りますが、複数回派生すると、無駄なデータが大量に発生します。したがって、派生クラスを構築するための基本クラスとして既存のクラスをランダムに検索しないでください。
- 基本クラスから受け取ったメンバーを調整します。継承を通じて派生クラスの基本クラス メンバーのアクセス属性を変更できる一方で、派生クラスの基本クラス メンバーと同じ名前のメンバーを宣言して、そのメンバーを保護することができます。同じ名前の基本クラスです。シールドの意味は、それを新しいメンバーに置き換えることです。古いメンバー。
- 派生クラスを宣言するときにメンバーを追加すると、派生クラスによる基本クラスの関数の拡張が反映されます。
- 派生クラスを宣言するときは、派生クラスのコンストラクターとデストラクターも自分で定義する必要があります。
4. 派生クラスのメンバーの属性にアクセスする
派生クラスには基本クラスのメンバーと派生クラスのメンバーが含まれているため、これら 2 つのメンバー間の関係と属性へのアクセスに問題が生じます。この関係は、基本クラスのメンバーのアクセス属性と派生クラスの継承メソッドの組み合わせによって決まります。
基本クラスからクラスを派生するための一般的な形式は次のとおりです。
1.公的継承
派生クラスの継承モードがパブリック(public)属性の場合、派生クラスでは、派生クラス内の基本クラスのパブリックメンバおよびプロテクトメンバのアクセス属性は変更されず、派生クラスのメンバは直接アクセスできません。基本クラスのプライベート メンバーにアクセスします。
class Point //基类Point类的声明
{
public: //公有函数成员
void InitP(float xx = 0, float yy = 0) {
X = xx; Y = yy; }
void Move(float xOff, float yOff) {
X += xOff; Y += yOff; }
float GetX() {
return X; }
float GetY() {
return Y; }
private: //私有数据成员
float X, Y;
};
class Rectangle : public Point //派生类声明
{
public: //新增公有函数成员
void InitR(float x, float y, float w, float h){
InitP(x, y); //调用基类公有成员函数
W = w; H = h;}
float GetH() {
return H; }
float GetW() {
return W; }
private: //新增私有数据成员
float W, H;
};
int main()
{
Rectangle rect;
rect.InitR(2, 3, 20, 10);
//通过派生类对象访问基类公有成员
rect.Move(3, 2);
cout << rect.GetX() << ','
<< rect.GetY() << ','
<< rect.GetH() << ','
<< rect.GetW() << endl;
return 0;
}
2. プライベート継承
派生クラスでは、基本クラスのパブリック メンバーと保護されたメンバーは、派生クラスのプライベート メンバーとして機能します。派生クラスのメンバーはこれらに直接アクセスできますが、派生クラスのメンバーは基本クラスのプライベート メンバーに直接アクセスできません。 。プライベート継承の後、すべての基本クラスのメンバーは派生クラスのプライベート メンバーまたはアクセス不能なメンバーになり、それ以上派生できなくなります。
3. メンバーの保護と継承の保護
派生クラスの継承モードが保護継承属性の場合、派生クラスでは、基本クラスのパブリック メンバーと保護されたメンバーは派生クラスの保護されたメンバーになります。派生クラスのメンバーはこれらに直接アクセスできますが、派生クラスのメンバーは、派生クラスは基本クラスにアクセスできません。クラスのプライベート メンバーです。保護された継承はさらに派生できますが、プライベート継承は派生できません。
結論は:
- 継承方法に関係なく、派生クラスのメンバー関数およびフレンド関数は、基本クラスのパブリック メンバーおよび保護されたメンバーにアクセスできますが、プライベート メンバーにはアクセスできません。
- パブリック継承では、派生クラスのオブジェクトは、基本クラスのパブリック メンバーにのみアクセスできます。
- 保護された継承とプライベート継承では、派生クラスのオブジェクトは基本クラスのメンバーにアクセスできません。
4. マルチレベル導出中の属性へのアクセス
マルチレベルの派生関係は図に示すとおりです。クラス A が基本クラス、クラス B がクラス A の派生クラス、クラス C がクラス B の派生クラスの場合、クラス C も派生クラスになります。クラスAの。クラス B はクラス A の直接派生クラスであり、クラス C はクラス A の間接派生クラスです。クラス A は、クラス B の直接基本クラスであり、クラス C の間接基本クラスです。
多级派生类的访问属性:
class A //基类
{
private:
int ka;
public:
int ia;
protected:
void fa( );
int ja;
};
class B: public A // public方式
{
private:
int mb;
public:
void fb1( );
protected:
void fb2( );
};
class C: protected B // protected方式
{
private:
int nc;
public:
void fc1( );
};
5. 派生クラスのコンストラクターとデストラクター
1. 単純な派生クラスのコンストラクター
単純な派生クラスには、基本クラスと派生レベルが 1 つだけあり、派生クラスのデータ メンバーには基本クラスのオブジェクト (サブオブジェクト) が含まれません。派生クラスのコンストラクターを定義するときは、独自のデータ メンバーを初期化するだけでなく、基本クラスのコンストラクターを呼び出して、基本クラスのデータ メンバーを初期化する必要もあります。
コンストラクターの形式は次のとおりです。 派生クラス名:: 派生クラス名 (基底クラスに必要な仮パラメーター、このクラスのメンバーに必要な仮パラメーター): 基底クラス名 (基底クラスのパラメーター リスト) {初期化代入文このクラスのメンバー;} ;
基本クラスのパラメーター テーブルには、基本クラス コンストラクターに渡される実際のパラメーター (派生クラス コンストラクターの合計パラメーター テーブル内のパラメーター) がリストされます。派生クラス コンストラクターの仮パラメーターを、基本クラス コンストラクターの実パラメーターとして使用します。
単純な派生クラスのコンストラクター
class B
{
private:
int b;
public:
B();
B(int i);
void Print() const;
};
class C:public B
{
private:
int c;
public:
C();
C(int i, int j);
void Print() const;
};
B::B()
{
b = 0; cout << "调用B的默认构造函数." << endl;
}
B::B(int i)
{
b = i; cout << "调用的构造函数." << endl;
}
void B::Print() const
{
cout << b << endl;
}
C::C()
{
c = 0;
cout << "调用C的默认构造函数." << endl;
}
C::C(int i, int j) :B(i)
{
c = j;
cout << "调用C的构造函数." << endl;
}
void C::Print() const
{
B::Print(); cout << c << endl;
}
void main()
{
C obj(5, 6);
obj.Print();
}
2. サブオブジェクトを持つ派生クラスのコンストラクター
標準型や文字列などのシステム提供型に加えて、クラスのデータ メンバーをクラス型にすることもできます。たとえば、クラスを宣言する場合、それにはクラス型のデータ メンバーが含まれます: Student s1; の一般的な形式派生クラス コンストラクター: 派生クラス名
:: 派生クラス名 (パラメーター リスト全体): 基本クラス名 (実際のパラメーター リスト)、サブオブジェクト名 (パラメーター リスト) {派生クラスの新しいメンバーの初期化ステートメント;}
派生クラス コンストラクターのタスクには次が含まれます。
-
基本クラスのデータメンバーを初期化する
-
子オブジェクトのデータメンバーを初期化する
-
派生クラスのデータ メンバーの初期化
注:不能在声明派生类时对子对象初始化,系统在建立派生类对象时调用派生类构造函数对子对象进行初始化。
派生クラスのコンストラクターが実行される順序は次のとおりです。
-
基本クラスのコンストラクターを呼び出して、基本クラスのデータ メンバーを初期化します。
-
サブオブジェクト コンストラクターを呼び出して、サブオブジェクト データ メンバーを初期化します。
-
派生クラスのコンストラクターを実行し、派生クラスのデータ メンバーを初期化します。
注:编译系统在此根据参数名(而不是参数的顺序)决定各参数表中参数之间的传递关系。如有多个子对象,要逐个列出子对象及其参数表。
#include <iostream>
#include <string>
using namespace std;
class Student //声明基类
{
public: //公用部分
Student(int n, string nam) //基类构造函数
{
num = n; name = nam;}
void display()
{
cout << "学号:" << num << endl << "姓名:" << name << endl;}
protected: //保护部分
int num;
string name;
};
class Student1 : public Student // public继承方式
{
private: // 派生类的私有数据
Student monitor; // 定义子对象(班长)
int age;
string addr;
public:
Student1(int n, string nam, int n1, string nam1, int a, string ad) :Student(n, nam), monitor(n1, nam1){
age = a; addr = ad;}
void show()
{
cout << "这个学生是:" << endl;
display(); // 输出num和name
cout << "年龄: " << age << endl;
cout << "地址: " << addr << endl;
}
void show_monitor()
{
cout << endl << "班长是:" << endl;
monitor.display(); //调用基类成员函数
}
};
int main()
{
Student1 stud1(101, "王力",110,"李军",19,"上海市北京路115号");
stud1.show(); // 输出第一个学生的数据
stud1.show_monitor(); // 输出子对象的数据
return 0;
}
3. マルチレベル導出におけるコンストラクター
クラスは派生クラスを派生でき、派生クラスは派生を続けて派生階層を形成できます。
可以按照前面派生类构造函数的规则逐层写出各个派生类的构造函数。
基类的构造函数首部:
Student(int n, string nam );
派生类Student1的构造函数首部:
Student1(int n,string nam,int a):Student(n,nam);
派生类Student2的构造函数首部:
Student2(int n,string nam,int a,int s):Student1(n,nam,a);
写派生类构造函数的规则是,只须调用其直接基类的构造函数即可,不要列出每一层派生类的构造函数。
在声明Student2类对象时,调用Student2构造函数,在执行Student2构造函数时,先调用Student1构造函数,在执行Student1构造函数时,先调用基类Student构造函数。
初始化的顺序是:
①先初始化基类的数据成员num和name
②再初始化Student1的数据成员age
③最后初始化Student2的数据成员score
class Student //声明基类
{
public:
Student(int n, string nam) //基类构造函数
{
num = n; name = nam;}
void display() //输出基类数据成员
{
cout << "num:" << num << endl; cout << "name:" << name << endl;}
protected: //保护部分
int num;
string name;
};
class Student1 : public Student //声明公用派生类Student1
{
public:
Student1(int n, string nam, int a) :Student(n, nam){
age = a;} //在此处只对派生类新增的数据成员初始化
void show() //输出num,name和age
{
display(); cout << "age: " << age << endl;}
private: //派生类的私有数据
int age; //增加一个数据成员
};
class Student2 :public Student1 //声明间接公用派生类student2
{
public:
Student2(int n, string nam, int a, int s) :Student1(n, nam, a)
{
score = s;}
void show_all() // 输出全部数据成员
{
show(); cout << "score:" << score << endl;}
private:
int score; //增加一个数据成员
};
int main()
{
Student2 stud(10010, "李明", 17, 89);
stud.show_all(); //输出学生的全部数据
return 0;
}
4. 派生クラスコンストラクターの特別な形式
class A
{
public:
A() {
a = 0; cout << "A类无参构造函数被调用" << endl; }
A(int i) {
a = i; cout << "A类有参构造函数被调用" << endl; }
void print() {
cout << a << ","; }
int b;
private:
int a;
};
class B : public A
{
public:
B() {
b1 = b2 = 0; }
B(int i) {
b1 = 0; b2 = i; }
B(int i, int j, int k) :A(i), b1(j), b2(k) {
}
void print(){
A::print(); cout << b1 << "," << b2 << endl;}
private:
int b1, b2;
};
int main()
{
B b1, b2(5), b3(1, 2, 3); b1.print(); b2.print(); b3.print();
return 0;
}
5. 派生クラスのデストラクタ
クラスを派生する場合、派生クラスは基本クラスのデストラクターを継承できません。派生クラスのオブジェクトを削除する場合、派生クラスのデストラクターは基本クラスのデストラクターを呼び出す必要があります。
デストラクタのシーケンスは次のとおりです。最初に派生クラスのデストラクタ -> サブオブジェクトのデストラクタ -> 基本クラスのデストラクタを呼び出します。
class B
{
private:
int b;
public:
B() {
b = 0; cout << "调用B的默认构造函数." << endl; }
B(int i) {
b = i; cout << "调用B的构造函数." << endl; }
~B() {
cout << "调用B的析构函数." << endl; }
void Print() const {
cout << b << endl; }
};
class C :public B
{
private: int c;
public:
C() {
c = 0; cout << "调用C的默认构造函数." << endl; }
C(int i, int j) :B(i) {
c = j; cout << "调用C的构造函数." << endl; }
~C() {
cout << "调用C的析构函数." << endl; }
void Print() const {
B::Print(); cout << c << endl; }
};
void main()
{
C obj(5, 6);
obj.Print();
}
6. 多重継承
1 つの基本クラスのみから派生することを単一継承と呼びます。派生クラスが同時に 2 つ以上の基本クラスを持つ場合、それは多重継承と呼ばれます。
1. 複数の継承メソッドの宣言
class 派生クラス名: 継承モード 1 基底クラス名 1、継承モード 2 基底クラス名 2,...{メンバー宣言;}
注:每一个“继承方式”,只用于限制对紧随其后之基类的继承。若缺省,系统默认为私有继承方式。
class A
{
public:
void setA(int);
void showA();
private:
int a;
};
class B
{
public:
void setB(int);
void showB();
private:
int b;
};
void A::setA(int x) {
a=x; }
void B::setB(int x) {
b=x; }
class C : public A, private B
{
public:
void setC(int, int, int);
void showC();
private:
int c;
};
void C::setC(int x, int y, int z)
{
setA(x); setB(y); c=z;
}
int main()
{
C obj;
obj.setA(5);
obj.showA();
obj.setC(6,7,9);
obj.showC();
return 0;
}
2. 派生クラスのコンストラクターの多重継承
派生クラスのコンストラクター形式: 派生クラス コンストラクター名 (パラメーター リスト全体): 基本クラス 1 コンストラクター (パラメーター リスト)、基本クラス 2 コンストラクター (パラメーター リスト)、基本クラス 3 コンストラクター (パラメーター リスト)... { 派生初期化ステートメントクラスの新しいメンバー }
注:1.各基类的排列顺序不分先后,系统调用基类构造函数的顺序就是声明派生类时基类的出现顺序。2.多继承析构函数的执行顺序与多继承方式下构造函数的执行顺序完全相反,首先对派生类新增的数据成员进行清理,再对派生类对象成员进行清理,最后才对基类继承来的成员进行清理。
class Base1
{
int x;
public:
Base1(int a) {
x = a; cout << "1的构造函数!\n"; }
~Base1() {
cout << "1的析构函数!\n"; }
};
class Base2
{
int y;
public:
Base2(int a) {
y = a; cout << "2的构造函数!\n"; }
~Base2() {
cout << "2的析构函数!\n"; }
};
class Derived :public Base2, public Base1
{
int z; Base1 b1, b2;
public:
Derived(int a, int b) :
Base1(a), Base2(20), b1(200), b2(a + b){
z = b; cout << "派生类的构造函数!\n";}
~Derived() {
cout << "派生类的析构函数!\n"; }
};
void main(void)
{
Derived c(100, 200);
}
3. 多重継承によるあいまいさ
多重継承に関する最も一般的な問題は、派生クラスが同じ名前の基本クラスのメンバーを継承することによって生じるあいまいさです。
注:1.在多重继承时,基类与派生类之间,或基类之间出现同名成员时,将出现访问时的二义性(不确定性)——采用虚函数或同名隐藏规则来解决。2.当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,将产生二义性——采用虚基类来解决。
class B1
{
public:
int nV;
void fun(){
cout << "Member of B1" << endl;}
};
class B2
{
public:
int nV;
void fun(){
cout << "Member of B2" << endl;}
};
class D1 : public B1, public B2
{
public:
int nV; //同名数据成员
void fun(){
cout << "Member of D1" << endl;}
};
void main()
{
D1 d1;
d1.nV = 1;
d1.fun();
d1.B1::nV = 2;
d1.B1::fun();
d1.B2::nV = 3;
d1.B2::fun();
}
同名隠蔽ルール - 派生クラスと基本クラスに同じメンバーが存在する場合:
- 名前が強制されない場合、派生クラス内の同じ名前のメンバーが派生クラス オブジェクトを通じて使用されます。
- 派生クラス オブジェクトを通じて、同じ名前を持つ基本クラスのオーバーライドされたメンバーにアクセスする場合は、基本クラス名修飾を使用する必要があります。
4. 仮想基本クラス
派生クラスに複数の直接基本クラスがあり、これらの直接基本クラスに共通の基本クラスがある場合、この間接共通基本クラスのデータ メンバーと同じ名前の複数のメンバーが派生クラスに保持されます。派生クラスで同じ名前の間接共通基本クラスの複数のメンバーを保持したくない場合、C++ は、間接共通基本クラスを継承するときに派生クラスが 1 つのメンバーのみを保持できるようにする仮想基本クラス メソッドを提供します。
パラメーターを持つコンストラクターが仮想基本クラスで定義され、デフォルトのコンストラクターが定義されていない場合、仮想基本クラスは、そのすべての派生クラス (直接および間接) のコンストラクターの初期化テーブルを通じて初期化される必要があります。
class A
{
A (int k) {
} … … };
class B: virtual public A
{
B (int n ):A(n){
}… … };
class C: virtual public A
{
C (int n ):A(n){
} … … };
class D: public B,public C
{
D (int n ):A(n),B(n),C(n) {
} … … };
class A
{
public:
int x;
A(int a = 0) {
x = a; }
};
class B :public virtual A
{
public:
int y;
B(int a = 0, int b = 0) : A(a) {
y = b; }
};
class C :public virtual A
{
public:
int z;
C(int a = 0, int c = 0) :A(a) {
z = c; }
};
class D :public B, public C
{
public:
int dx;
D(int a1, int b, int c, int d, int a2) :B(a1, b), C(a2, c){
dx = d;}
};
void main(void)
{
D d1(10, 20, 30, 40, 50);
cout << d1.x << endl;
d1.x = 400;
cout << d1.x << endl;
cout << d1.y << endl;
}
仮想基本クラスのコンストラクターの呼び出し順序の規則は次のとおりです。
- 同じ階層に複数の仮想基本クラスのみが含まれる場合は、それらを宣言された順序で呼び出してから、派生クラスのコンストラクターを呼び出します。
- 仮想基本クラスが非仮想基本クラスから派生している場合、最初に非仮想基本クラスのコンストラクターが呼び出され、次に派生クラスのコンストラクターが呼び出されます。
- 同じレベルに仮想基本クラスと非仮想基本クラスの両方が含まれる場合、最初に仮想基本クラスのコンストラクターが呼び出され、次に非仮想基本クラスのコンストラクターが呼び出され、最後に派生クラスのコンストラクターが呼び出されます。
複数の継承コンストラクターの呼び出し順序の規則は次のとおりです。
- 仮想基本クラスのコンストラクターは、仮想基本クラスが継承される順序で呼び出します。
- 非仮想基本クラスのコンストラクターは、非仮想基本クラスから継承された順序で呼び出します。
- メンバー オブジェクトのコンストラクターは、宣言された順序で呼び出します。
- 派生クラス独自のコンストラクターを呼び出します。
class OBJ1
{
public: OBJ1() {
cout << "调用OBJ1类构造函数" << endl; }
};
class OBJ2
{
public: OBJ2() {
cout << "调用OBJ2类构造函数" << endl; }
};
class Base1
{
public: Base1() {
cout << "调用Base1类构造函数" << endl; }
};
class Base2
{
public: Base2() {
cout << "调用Base2类构造函数" << endl; }
};
class Base3
{
public: Base3() {
cout << "调用Base3类构造函数" << endl; }
};
class Base4
{
public: Base4() {
cout << "调用Base4类构造函数" << endl; }
};
class Derived :public Base1, virtual public Base2,public Base3, virtual public Base4
{
public:
Derived() :Base4(), Base3(), Base2(), Base1(), obj1(), obj2(){
cout << "调用派生类构造函数成功!" << endl;}
protected:
OBJ1 obj1;
OBJ2 obj2;
};
int main()
{
Derived aa;
cout << "派生类对象 aa 构造成功,谢谢!" << endl;
return 0;
}
7. 型の互換性規則
1. 割り当ての互換性ルール
パブリック派生クラスのオブジェクトを基本クラスのオブジェクトとして使用することはできますが、その逆は禁止されており、これを代入互換性ルールと呼びます。
具体的には:
- 派生クラスのオブジェクトは、基本クラスのオブジェクトに割り当てることができます。
- 派生クラスのオブジェクトは、基本クラス オブジェクトへの参照を割り当てたり、初期化したりできます。
- 関数パラメータは基本クラス オブジェクトまたはその参照であり、対応する実際のパラメータは派生クラス オブジェクトを使用できます。
- 基本クラス オブジェクトへのポインターは、派生クラス オブジェクトを指すこともできます。
- 基本クラスから継承されたメンバーのみが、基本クラスのオブジェクト名とポインターを通じて使用できます。
如(1):
A a1; // 定义基类 A 对象 a1
B b1; // 定义类 A 的公用派生类 B 的对象 b1
a1=b1; // 用派生类 B 对象 b1 对基类对象 a1 赋值,在赋值时舍弃派生类自己的成员 。
注:赋值后不能企图通过对象 a1 去访问派生类对象 b1 的成员,因为 b1 的成员与 a1 的成员是不同的。
割り当ての互換性ルール:
- サブクラス オブジェクトを使用できるのは、その基本クラス オブジェクトに値を割り当てる場合のみですが、基本クラス オブジェクトを使用してそのサブクラス オブジェクトに値を割り当てることはできません。
- 同じ基本クラスの異なる派生クラスのオブジェクト間で値を割り当てることはできません。
如(2):
A a1; // 定义基类 A 对象 a1
B b1; // 定义公用派生类 B 对象 b1
A&r=a1; // 定义基类 A 对象的引用,并用 a1 初始化。
注:1.可以用子类对象初始化引用变量 r,将最后一行改为: A& r=b1;2.保留上面第 3 行 “A& r=a1;” ,而对 r 重新赋值: r=b1;
如(3):
函数fun:
void fun(A& r) // 形参是类 A 的对象的引用
{
cout<<r.num<<endl; }
由于子类对象与派生类对象赋值兼容,派生类对象能自动转换类型,在调用 fun 函数时可以用派生类 B 的对象 b1 作实参 :
fun(b1); 输出类 B 的对象 b1 的基类数据成员 num 的值。
如(4):指向基类对象的指针,也可以指向派生类对象。
class Student
{
public:
Student(int, string, float);
void display();
private:
int num;
string name;
float score;
};
Student::Student(int n, string nam, float s)
{
num = n; name = nam; score = s;}
void Student::display()
{
cout << endl << "num:" << num << endl << "name:" << name << endl << "score:" << score << endl;}
class Graduate :public Student
{
public:
Graduate(int, string, float, float);
void display();
private:
float pay;// 工资
};
Graduate::Graduate(int n, string nam, float s, float p) : Student(n, nam, s), pay(p) {
}
void Graduate::display() {
Student::display(); cout << "pay = "<< pay << endl; }
void main()
{
Student stud1(1001, "Li", 87.5);
Graduate grad1(2001, "Wang", 98.5, 563.5);
Student *pt = &stud1;
pt->display(); // 调用 stud1.display 函数
pt = &grad1; // 指针指向 grad1
pt->display(); // 调用 grad1.display 函数
}
2. 継承と結合
別のクラスのオブジェクトをあるクラスのデータ メンバーとして使用することをクラス合成といいます。クラスの構成は、継承と同様、ソフトウェアを再利用する重要な方法です。継承を通じて、派生クラスと基本クラスの間に「はい」関係を確立します。メンバー クラスと複合クラスの間には、組み合わせによって確立される「そこに」関係があります。継承は垂直的であり、合成は水平的です。
class Teacher// 教师类
{
public: …
private:
int num;
string name;
char sex;
};
class BirthDate
{
public:
private:
int year;
int month;
int day;
};
class Professor: public Teacher
{
public: …
private:
BirthDate birthday;
};
Professor 类通过继承,从 Teacher 类得到了 num, name,age,sex 等数据成员。
通过组合,从 BirthDate 类得到了 year,month,day 等数据成员 。