程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++ primer讀書筆記10-繼承

C++ primer讀書筆記10-繼承

編輯:C++入門知識

C++ primer讀書筆記10-繼承


封裝,繼承,多態是C++的三大基本概念,這裡著重總結一下繼承相關的東西


1 類派生列表
類派生列表指定派生類要繼承的基類,派生列表中有一個或者多個基類如:
class B : public A1,protected A2,private A3
但是單繼承時最常見的,多繼承不多見


2 派生類的定義
派生類繼承時,會包含父類的所有成員,即便私有成員不能被訪問。父類中的虛函數,在派生類中一般也要定義,如


果不定義的話,派生類將繼承基類的虛函數


3 基類必須是已經定義的
一個僅僅聲明的類,是不能出現在派生類的類派生列表中。因此派生類很可能要訪問基類的列表,但是如果基類還沒


被定義,所以這樣做是錯誤的。


4 派生類的聲明
派生類的列表不能出現在派生類的列表中,如:
class B : public A; //error
正確的聲明應是:
Class B;
Class A;


5 繼承修飾符
C++中的繼承方式有三種,public,protected,private繼承(簡稱3p)。不管是何種繼承方式,基類中的private成員


都不能被派生的用戶代碼和成員定義代碼訪問。接下來具體說說三種繼承方式:
<1>private繼承,基類的所有成員在派生類中都是private成員,但是基類的private成員依舊不能被訪問
<2>protected繼承,基類的public,protected成員變成子類的protected成員,基類的private成員依舊不能被訪問
<3>public繼承,基類的public變成派生類的public,protected變成派生類的protected,基類的private不能被訪問


6 protected 修飾符
當protected修飾類成員的時候,類成員可以被類自身成員定義所使用,但是不能被用戶代碼使用,這點類似private
其次,protected修飾的成員可以被子類訪問到,這點類似public,但是不用於private。


7 不能被繼承的成員
構造函數,復制控制成員(復制構造函數,賦值操作符,析構函數)這是不能被繼承的函數


8 派生類的構造函數
派生類構造函數一般格式:
DeriveClass(xxx):baseClass(xxx),x1(x),x2(x)....
<1> 派生類的構造函數除了要初始化自己新定義的成員外,還要初始化基類的成員,順序是先調用基類的構造函數初始化基類的成員,然後再初始化自己的新成員(順序是聲明的順序)
<2> 如果沒有自己定義構造函數,編譯器會合成一個默認的構造函數,先調用基類的默認構造函數,然後就再去初始化其他新的成員。
<3> 這裡需要注意的是:如果基類沒有默認構造函數,那麼編譯器就不能合成默認構造函數,如果這個時候再不去定義派生類的默認構造函數,那樣就會出錯。諸如--“error C2512: “Derive”: 沒有合適的默認構造函數可用。”
<4> 如果不在派生類構造函數的初始化列表中指定基類的構造函數,則會調用基類的默認構造函數
<5> 派生類構造函數的默認實參派生類可以將構造函數的所有參數都設置為默認參數,可以使用0-n個默認參數

9 派生類中的復制控制
復制控制包括復制構造函數,賦值操作符,析構函數
<1>沒有定義復制構造函數,編譯器會自己合成一個復制構造函數,它會調用基類的復制構造函數
<2>自定義復制構造函數,一般要調用基類的復制構造函數,否則會出意外
<3>沒有定義賦值操作符,編譯會自動合成一個賦值操作符,它會調用基類的復制操作符
<4>自定義賦值操作符要提防自身賦值,一般都是這麼做
Derive& Derived::operator =(Derived& rh)
{
if(this != &rh)
{
Base::operator=(rh);
//do whatever needed to clean up the old value
//assign the members from the derived
}
}

這樣做的原因就是為了防止自身賦值,在自身賦值的時候,我們通常要先把動態開辟的內存clear掉,如果不加上if(this != &rh)的話,那麼在進行自身賦值的時候,就會把自己給廢了(clear掉自己)。
<5> 派生類析構函數
這個和前兩個不同,派生類的析構函數只負責自己新定義對象的析構,不負責基類對象的析構。在派生類對象析構的時候,編譯器會先調用派生類自己的析構函數,然後調用基類的析構函數。每個類不管基類還是派生類,都只負責清除自己的成員。
以上的復制控制示例代碼如下所示:
#pragma once
#include 
using namespace std;
class Base
{
public:
Base(){ m = 10;cout<<"Base default constructor"<m = base.m;cout<<"the base operator ="<
執行結果:
Base default constructor
this is the separtor-----------------
the base copy constructor
the base operator =
Derived destructor
Base destructor
Derived destructor
Base destructor


10 派生類函數的調用
在繼承的情況下,派生類的作用域嵌套在基類的作用域中,如果不能再派生類中確定名字,就會在外圍的基類中朝趙名字的定義。
<1>派生類調用函數是這樣的原則:先在派生類中查找名字,查到就停止;查不到的就在基類中查找。
<2>靜態類型,動態類型
靜態類型:是指不需要考慮表達式的執行期語義,僅分析程序文本而決定的表達式類型。靜態類型僅依賴於包含表達式的程序文本的形式,而在程序運行時不會改變
動態類型:由一個左值表達式表示的左值所引用的最終派生對象的類型。一個右值表達式的動態類型,就是它的靜態類型。
<3>對象,引用或者指針的靜態類型決定了對象能夠執行的行為
因此引用或者指針不能執行派生類中新定義的成員名字。
<4>基類與派生類的名字發生沖突
基類與派生類中有相同的名字的時候,派生類會屏蔽基類中同名的成員。如果非要調用基類的成員,那麼就必須顯式的調用,如:Base::func();
<5>基類成員函數的屏蔽
基類和派生類有相同名字的函數,但是原型不一樣(參數),那麼派生類將不能直接調用基類的那個同名的函數。根據之前的原則,找到名字相同的就不再往基類找了,所以下面的程序是錯誤的。
class A
{
public:
void fun(){};
}
class B : public A
{
public:
void fun(int){};
private int x;
}
B b;
b.fun(11); //OK
b.func();  //error

11 using
可以在派生類中使用using來改變繼承自基類中的成員的級別,前提是派生類對基類的成員具有訪問權限。
//Base.h
#pragma once
#include 
using namespace std;
class Base
{
public:
Base(void){ n = 100; };
~Base(void){};
size_t size()const{return n;}
protected:
//private:
size_t n;
int fn(int x){return x;};
int fn(){return 11;}
};

//Derived.h
#pragma once
#include "base.h"
class Derived :
private Base
{
public:
Derived(void){};
~Derived(void){};
using Base::size;
using Base::fn;
};

//main.cpp
#include "Base.h"
#include "Derived.h"
#include 
using namespace std;

void main()
{
Derived XX;
Base YY;
cout<
在這裡我們也可以看到如果基類的函數有重載的話,針對同一個using Base::XXX,可以使所用的名字為XXX的基類函數在派生類中得到聲明。真可可謂一次聲明,多個使用。


12 friend 跟繼承的關系
友元跟繼承沒有關系。基類的派生類不能被基類的派生類訪問。友元類的派生類對基類沒有訪問權限。


13 引用轉換,轉換對象
引用轉換:派生類對象轉換為基類類型的引用
轉換對象:用派生類對象轉換為基類的對象,這個時候形參是固定的,編譯和運行時候的對象都是基類類型對象。派生類的基類部分被復制到形參中。
引用轉換:派生類對象轉換為基類的引用。但是如果賦值的話也是把派生類的基類部分賦值給基類對象,這和指針的切片效應一個道理。


14 基類和派生類的轉換
派生類轉對象賦給基類也是有條件才能行的,這個條件就是派生類是通過public來繼承的,這樣的話不論是在成員代碼還是在用戶代碼中,都能實現
Derived d;
Base b = d;

如果是通過protected繼承的,那麼只能在成員定義中使用這樣的代碼。


15 默認的繼承准則
類和結構體在默認的繼承准則方面剛好相反。
<1>類繼承的時候,如果不加限定符,那麼默認是private繼承,結構體不加限定符的時候,則是默認的public繼承。
<2>此外strut中從定義開始到內部的第一個限定符之間默認的是public修飾,但是class默認的是private修飾。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved