繼承和派生
1、繼承中的構造和析構的順序
#千鋒教育#
學習必須如蜜蜂一樣,採過許多花,這才能釀出蜜來。
class Base
{
public:
Base()
{
cout<<"父類的無參構造函數"<<endl;
}
~Base()
{
cout<<"父類中的析構函數"<<endl;
}
};
class Son:public Base
{
public:
Son()
{
cout<<"子類的無參構造"<<endl;
}
~Son()
{
cout<<"子類中的析構函數"<<endl;
}
};
void test01()
{
Son ob1;
}
運行結果:
總結:
構造順序: 父類(基類)構造 ------> 子類(派生類)構造
析構順序:子類(派生類)析構------> 父類 (基類) 析構
2、子類中 有父類、對象成員 構造和析構的順序
父類的構造和析構 對象成員的構造和析構 子類自身的構造和析構
class Other
{
public:
Other()
{
cout<<"對象成員的構造函數"<<endl;
}
~Other()
{
cout<<"對象成員的析構函數"<<endl;
}
};
class Base
{
public:
Base()
{
cout<<"父類的無參構造函數"<<endl;
}
~Base()
{
cout<<"父類中的析構函數"<<endl;
}
};
class Son:public Base
{
public:
Son()
{
cout<<"子類的無參構造"<<endl;
}
~Son()
{
cout<<"子類中的析構函數"<<endl;
}
Other ob;//對象成員
};
void test01()
{
Son ob1;
}
運行結果:
總結:(重要)
3、詳解 子類中的構造
1、子類會默認調用 父類的 無參構造
2、子類 必須顯示 使用初始化列表 調用 父類的有參構造
調用形式:父類名稱。
Son(int a,int b):Base(a),b(b)
{
//this->b = b;
}
class Base
{
private:
int a;
public:
Base()
{
cout<<"父類的無參構造函數"<<endl;
}
Base(int a)
{
this->a = a;
cout<<"父類的有參構造函數"<<endl;
}
~Base()
{
cout<<"父類中的析構函數"<<endl;
}
};
class Son:public Base
{
private:
int b;
public:
Son()
{
cout<<"子類的無參構造"<<endl;
}
Son(int b)
{
this->b = b;
cout<<"子類的有參構造函數int"<<endl;
}
//子類必須用 初始化列表 顯示的調用父類的有參構造
//父類名稱(參數)
Son(int a,int b):Base(a)//顯示的調用父類的有參構造
{
this->b = b;
cout<<"子類的有參構造函數 int int"<<endl;
}
~Son()
{
cout<<"子類中的析構函數"<<endl;
}
};
void test01()
{
//子類 默認 會調用 父類的無參構造
//Son ob1(10);
//子類必須用 初始化列表 顯示的調用父類的有參構造
//父類名稱+()
Son ob2(10,20);
}
運行結果:
案例提高:
如果父類有參構造:
Base(int a, int data)
{
this->a = a;
this->data = data;
cout<<"父類的有參構造函數"<<endl;
}
子類想調用 父類有參構造:
//子類必須用 初始化列表 顯示的調用父類的有參構造
//父類名稱(參數)
Son(int a,int b, int c):Base(a,c),b(b)//顯示的調用父類的有參構造
{
//this->b = b;
cout<<"子類的有參構造函數 int int"<<endl;
}
4、父類和子類的同名 成員變量 處理
4.1、當 父類和子類 成員變量同名時 在子類就近原則 選擇本作用域的子類成員
4.2、如果在子類中 必須使用父類中的同名成員 必須加上父類的作用域。
class Base
{
//父類的私有數據 一旦涉及繼承 在子類中不可見
public:
int num;
public:
Base(int num)
{
this->num = num;
cout<<"Base有參構造int"<<endl;
}
~Base()
{
cout<<"析構函數"<<endl;
}
};
class Son:public Base
{
private:
int num;
public:
Son(int num1,int num2):Base(num1)
{
this->num = num2;
cout<<"有參構造int int"<<endl;
}
~Son()
{
cout<<"析構函數"<<endl;
}
void showNum(void)
{
//如果在子類中 必須使用父類中的同名成員 必須加上父類的作用域
cout<<"父類中的num = "<<Base::num<<endl;
//當 父類和子類 成員變量同名時 在子類就近原則 選擇本作用域的子類成員
cout<<"子類中的num = "<<num<<endl;
}
};
void test01()
{
Son ob1(10,20);
ob1.showNum();
}
運行結果:
4.3、子類可以藉助 父類的公有方法 間接的操作 父類的私有數據(不可見的數據)
class Base
{
private:
int num;//父類的私有數據 一旦涉及繼承 在子類中不可見
public:
Base(int num)
{
this->num = num;
cout<<"Base有參構造int"<<endl;
}
~Base()
{
cout<<"析構函數"<<endl;
}
int getNum(void)
{
return num;
}
};
class Son:public Base
{
private:
int num;
public:
Son(int num1,int num2):Base(num1)
{
this->num = num2;
cout<<"有參構造int int"<<endl;
}
~Son()
{
cout<<"析構函數"<<endl;
}
void showNum(void)
{
//如果在子類中 必須使用父類中的同名成員 必須加上父類的作用域
cout<<"父類中的num = "<<getNum()<<endl;
//當 父類和子類 成員變量同名時 在子類就近原則 選擇本作用域的子類成員
cout<<"子類中的num = "<<num<<endl;
}
};
void test01()
{
Son ob1(10,20);
ob1.showNum();
}
運行結果:
5、父類和子類的同名 成員函數 處理
案例:1子類繼承父類所有成員函數 和成員變量
class Base
{
public:
void func(void)
{
cout<<"父類中的void func"<<endl;
}
void func(int a)
{
cout<<"父類中的int func a = "<<a<<endl;
}
};
class Son:public Base
{
public:
};
void test01()
{
//爲啥構造和析構除外?父類的構造和析構 只有父類自己知道該怎麼做(構造和析構 系統自動調用)
//子類會繼承父類所有成員函數(構造和析構函數除外) 和成員變量
Son ob1;
ob1.func();//訪問的是父類的void func(void)
ob1.func(10);//訪問的是父類的func(int a)
}
案例:2子類和父類 同名成員函數
class Base
{
public:
void func(void)
{
cout<<"父類中的void func"<<endl;
}
void func(int a)
{
cout<<"父類中的int func a = "<<a<<endl;
}
};
class Son:public Base
{
public:
//一旦子類 實現了 父類的同名成員函數 將屏蔽所有父類同名成員函數
void func(void)
{
cout<<"子類中voidfunc"<<endl;
}
};
void test01()
{
//爲啥構造和析構除外?父類的構造和析構 只有父類自己知道該怎麼做(構造和析構 系統自動調用)
//子類會繼承父類所有成員函數(構造和析構函數除外) 和成員變量
Son ob1;
ob1.func();
//ob1.func(10);//err //一旦子類 實現了 父類的同名成員函數 將屏蔽所有父類同名成員函數
//如果用戶 必須要調用父類 的同名成員函數 必須加作用域
ob1.Base::func();//調用父類的void func
ob1.Base::func(10);//調用父類的int func
}
int main(int argc, char *argv[])
{
test01();
return 0;
}
運行結果:
6、繼承中的靜態成員特性(瞭解)
class Base
{
public:
//靜態成員屬於類 而不屬於對象
static int num;
static int data;
static void showData(void);
};
int Base::num = 100;
int Base::data = 200;
class Son:public Base
{
public:
static int data;//父和子類 靜態成員 同名
static void showData(void);
};
int Son::data = 300;
void test01()
{
//從Base類中訪問
cout<<Base::num<<endl;
// Son 也擁有了靜態成員num
cout<<Son::num<<endl;
//父和子類 靜態成員 同名 在子類中 訪問子類中的成員
cout<<Son::data<<endl;//200
//父和子類 靜態成員 同名 訪問父類中的成員 必須加 Base::
cout<<Son::Base::data<<endl;//200
//父和子類 同名靜態成員函數 子類默認訪問子類的靜態成員函數
Son::showData();
//父和子類 同名靜態成員函數 子類訪問父類的靜態成員函數 必須加 Base::
Son::Base::showData();
}
運行結果:
7、多繼承(瞭解)
7.1、多繼承的格式:
class 子類: 繼承方式1 父類名1,繼承方式2 父類名2,繼承方式3 父類名3,....
{
};
//表示子類 是由 父類名1,父類名2,父類名3...共同派生出來
class Base1
{
public:
int a;
};
class Base2
{
public:
int b;
};
class Son:public Base1,public Base2
{
//Son類 擁有了a b
};
int main(int argc, char *argv[])
{
Son ob;
ob.a = 100;
ob.b = 200;
return 0;
}
7.2、多繼承容易產生二義性: (解決辦法1 使用作用域)
class Base1
{
public:
int a;
};
class Base2
{
public:
int a;
};
class Son:public Base1,public Base2
{
};
int main(int argc, char *argv[])
{
Son ob;
//ob.a = 100;//err Base1 和 Base2中都有a成員同名
//解決辦法:加作用域
ob.Base1::a = 100;
ob.Base2::a = 200;
return 0;
}
7.3、菱形繼承(具有公共祖先 的多繼承)
class Animal
{
public:
int data;
};
class Sheep:public Animal
{
public:
};
class Tuo:public Animal
{
public:
};
class SheepTuo:public Sheep,public Tuo
{
public:
};
int main(int argc, char *argv[])
{
SheepTuo st;
//SheepTuo 從Sheep中繼承data 從Tuo繼承data 就產生二義性
//st.data = 200;//err
//第一中方式:加作用域解決
st.Sheep::data = 200;
st.Tuo::data = 300;
return 0;
}
8、普通繼承:vs studio分析
class Animal
{
public:
int data;
};
class Sheep:public Animal
{
public:
};
class Tuo:public Animal
{
public:
};
class SheepTuo:public Sheep,public Tuo
{
public:
};
9、虛繼承(瞭解) vs studio分析
virtual修飾繼承方式
//繼承的動作 虛繼承
//父類:虛基類
class 子類:virtual public 父類
{
};
虛繼承:
class Animal
{
public:
int data;
};
class Sheep:virtual public Animal
{
public:
};
vbptr(虛基類指針) 其中v是virtual 虛 b是base 基類 prt指針
(vbptr指向虛基類表)
vbtable(虛基類表 ) 保存了當前的虛指針相對於虛基類的首地址的偏移量
class Tuo:virtual public Animal
{
public:
};
總結:之所以 產生 vbptr和vbtable 目的 保證 不管多少個繼承 虛基類的數據只有一份。
class SheepTuo:public Sheep,public Tuo
{
public:
};
案例1:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include<string.h>
using namespace std;
class Animal
{
public:
int data;
};
class Sheep :virtual public Animal
{
public:
};
class Tuo :virtual public Animal
{
public:
};
class SheepTuo :public Sheep, public Tuo
{
public:
};
int main(int argc, char* argv[])
{
SheepTuo st;
st.data = 200;
//通過Sheep的vbptr 尋找vbptr距離虛基類首地址的偏移量
//&st == vbptr
//*(int *)&st sheep 的虛基類表的起始位置
int off_set = (int)*((int*)(*(int*)&st) + 1);
cout << off_set << endl;
//通過sheep的vbptr 和 off_set定位虛基類的首地址
cout << ((Animal*)((char*)&st + off_set))->data << endl;
return 0;
}
注意:vsstudio中運行
注意: 虛繼承只能解決具備公共祖先的多繼承所帶來的二義性問題,不能解決沒有公共祖先的多繼承的。
虛繼承:不管繼承多少次 虛基類 只有一份數據。
各位看官,感覺我寫的怎麼樣呢, 大家有沒有看懂呢, 歡迎評論
如果對大家有幫助,不要忘記點個贊哦.