C++ 總結(jié) (二、面向?qū)ο?

C++ 總結(jié) (二、面向?qū)ο?

官網(wǎng)

本文總結(jié)面向?qū)ο蟛糠帧?/p>

Class 類

是由結(jié)構(gòu)體擴(kuò)展而來,內(nèi)部成員包含 實(shí)例變量函數(shù)
對(duì)象 是類實(shí)例,類是它的類型,對(duì)象是變量。
類成員(實(shí)例變量和函數(shù))有三種訪問權(quán)限: private、protectedpublic

  • private - 僅僅類內(nèi)部可以訪問(類成員默認(rèn)訪問權(quán)限)
  • protected - 僅僅類內(nèi)部和其子類可以訪問
  • public - 任何對(duì)象可見的地方都可以訪問

簡(jiǎn)單來說,定義和聲明一個(gè)類與變量 class Rectangle rect 格式如下:

class Rectangle {
    int width, height;  // 默認(rèn)private 訪問權(quán)限
  public:
    void set_values (int,int);
    int area (void);
} rect;

如上,類中的方法可以只聲明,具體實(shí)現(xiàn)放到外部,外部寫實(shí)現(xiàn)可使用域操作符實(shí)現(xiàn)。完整實(shí)現(xiàn)如下:

// classes example
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    void set_values (int,int);
    int area() {return width*height;}
};

// 在外部實(shí)現(xiàn)類中的 set_values 函數(shù)
void Rectangle::set_values (int x, int y) {
  width = x;
  height = y;
}

int main () {
  Rectangle rect;
  rect.set_values (3,4);
  cout << "area: " << rect.area();
  return 0;
}

// -----------------
area 12

構(gòu)造器(類初始化器)

想一下,上面例子中如果沒有調(diào)用 rect.set_values (3,4);直接獲取 area 會(huì)怎樣?

構(gòu)造器是類進(jìn)行初始化的一個(gè)函數(shù)。它只會(huì)在初始化類對(duì)象時(shí)調(diào)用一次,它沒有返回值,僅僅是用來初始化對(duì)象。構(gòu)造器也是類的一個(gè)成員函數(shù),它會(huì)在創(chuàng)建對(duì)象的時(shí)候自動(dòng)調(diào)用,無法被顯示調(diào)用。構(gòu)造器函數(shù)名稱同類名一樣,且無任何返回值(即使是void)

// example: class constructor
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle (int,int); // 構(gòu)造器函數(shù)
    int area () {return (width*height);}
};

Rectangle::Rectangle (int a, int b) {
  width = a;
  height = b;
}

int main () {
  Rectangle rect (3,4);
  Rectangle rectb (5,6);
  cout << "rect area: " << rect.area() << endl;
  cout << "rectb area: " << rectb.area() << endl;
  return 0;
}

// 注意,此例中么有 set_values 函數(shù),但是有一個(gè)相同效果的構(gòu)造器函數(shù)。

構(gòu)造器重載

構(gòu)造器函數(shù)和普通函數(shù)一樣可以構(gòu)成重載(參數(shù)數(shù)量不同/參數(shù)類型不同)。

默認(rèn)構(gòu)造器: 與類名同名函數(shù), 無參數(shù),無返回值。其特殊在于: 當(dāng)類對(duì)象聲明的時(shí)會(huì)自動(dòng)調(diào)用默認(rèn)初始化器

// 對(duì)象聲明就會(huì)調(diào)用默認(rèn)初始化器
Rectangle rectb;   // ok, default constructor called
// 加括號(hào)是聲明一個(gè)無參函數(shù)的語法(返回值是Rectangle類型)
Rectangle rectc(); // oops, default constructor NOT called 

統(tǒng)一初始化

初始化類對(duì)象有幾種方式:初始化器方式,統(tǒng)一初始化方式、賦值聲明方式等

如果初始化器只有一個(gè)參數(shù),初始化可以調(diào)用變量賦值的方式,語法如下

class_name object_name = initialization_value;

在 C++ 中引入了統(tǒng)一初始化方式,其與函數(shù)式初始化相似,區(qū)別在于使用的是花括號(hào){},而不是小括號(hào)()。在變量名與花括號(hào)之間可以可選使用 = 。語法如下

class_name object_name { value, value, value, ... }
class_name object_name = { value, value, value, ... } // 有無等號(hào)均可

一個(gè)完整的類初始化DEMO,包含四種初始化方式

// classes and uniform initialization
#include <iostream>
using namespace std;

class Circle {
    double radius;
  public:
    Circle(double r) { radius = r; }
    double circum() {return 2*radius*3.14159265;}
};

int main () {
  Circle foo (10.0);   // functional form
  Circle bar = 20.0;   // assignment init.
  Circle baz {30.0};   // uniform init.
  Circle qux = {40.0}; // POD-like

  cout << "foo's circumference: " << foo.circum() << '\n';
  return 0;
}
// --------------
foo's circumference: 62.8319

構(gòu)造器中的成員初始化

構(gòu)造器初始化 class 其他成員有幾種方式,直接賦值成員變量 或者 使用初始化列表。

class Rectangle {
    int width,height;
  public:
    Rectangle(int,int);
    int area() {return width*height;}
};

// 方式1: 直接操作class 內(nèi)部成員
Rectangle::Rectangle (int x, int y) { width=x; height=y; }

// 方式2: 使用 : 在參數(shù)與方法體 之間創(chuàng)建初始化列表
Rectangle::Rectangle (int x, int y) : width(x) { height=y; }

// 方式3: 同2
Rectangle::Rectangle (int x, int y) : width(x), height(y) { }

如果成員是基本類型,是否使用初始化列表的方式?jīng)]有區(qū)別,因?yàn)榛绢愋蜔o默認(rèn)構(gòu)造器
如果成員是class類型,必須在初始化列表中調(diào)用初始化,否則它會(huì)自動(dòng)調(diào)用默認(rèn)構(gòu)造器。

成員為 class 的初始化 Demo

// member initialization
#include <iostream>
using namespace std;

class Circle {
    double radius;
  public:
    Circle(double r) : radius(r) { }
    double area() {return radius*radius*3.14159265;}
};

class Cylinder {
    Circle base;
    double height;
  public:
    Cylinder(double r, double h) : base (r), height(h) {}
    double volume() {return base.area() * height;}
};

int main () {
  Cylinder foo (10,20);

  cout << "foo's volume: " << foo.volume() << '\n';
  return 0;
}

// -----------
foo's volume: 6283.19

// 注意 - 初始化方式可以使用同一初始化
Cylinder::Cylinder (double r, double h) : base{r}, height{h} { }

使用指針指向類

使用 class 定義的新類型可以和其他基本類型一樣被使用,指針同樣可以指向類對(duì)象的地址,指針訪問對(duì)象內(nèi)部成員使用箭頭操作符(->)。

// pointer to classes example
#include <iostream>
using namespace std;

class Rectangle {
  int width, height;
public:
  Rectangle(int x, int y) : width(x), height(y) {}
  int area(void) { return width * height; }
};


int main() {
  Rectangle obj (3, 4);
  Rectangle * foo, * bar, * baz;
  foo = &obj;
  bar = new Rectangle (5, 6);
  baz = new Rectangle[2] { {2,5}, {3,6} };
  cout << "obj's area: " << obj.area() << '\n';
  cout << "*foo's area: " << foo->area() << '\n';
  cout << "*bar's area: " << bar->area() << '\n';
  cout << "baz[0]'s area:" << baz[0].area() << '\n';
  cout << "baz[1]'s area:" << baz[1].area() << '\n';       
  delete bar;
  delete[] baz;
  return 0;
}

幾種運(yùn)算符說明

expression can be read as
*x pointed to by x
&x address of x
x.y member y of object x
x->y member y of object pointed to by x
(*x).y member y of object pointed to by x (equivalent to the previous one)
x[0] first object pointed to by x
x[1] second object pointed to by x
x[n] (n+1)th object pointed to by x

使用 struct 和 union 定義 class

類可以使用 struct 和 union 定義。

struct 定義類: 其成員變量默認(rèn)訪問權(quán)限為 public, class 默認(rèn)為 private
union 定義類: 內(nèi)部只能存儲(chǔ)一個(gè)成員變量,訪問權(quán)限為public,也可以有成員函數(shù)。

操作符重載

操作符本質(zhì)上是系統(tǒng)內(nèi)建的函數(shù),C++ 中可以對(duì)常用操作符進(jìn)行重載,來實(shí)現(xiàn)自定義的操作。格式

type operator sign (parameters) { /*... body ...*/ }

例如: 笛卡爾向量具有加法操作,使用 C++ 實(shí)現(xiàn)如下

// overloading operators example
#include <iostream>
using namespace std;

class CVector {
  public:
    int x,y;
    CVector () {}; // 默認(rèn)初始化器
    CVector (int a,int b) : x(a), y(b) {} // 指定初始化器
    CVector operator + (const CVector&);  // 重寫 + 運(yùn)算符函數(shù)聲明
};

CVector CVector::operator+ (const CVector& param) {
  CVector temp;
  temp.x = x + param.x;
  temp.y = y + param.y;
  return temp;
}

int main () {
  CVector foo (3,1);
  CVector bar (1,2);
  CVector result;
  result = foo + bar;
  cout << result.x << ',' << result.y << '\n';
  return 0;
}

// ----------------
4,3

// 注意 - 操作運(yùn)算符重寫后,顯隱式調(diào)用均可
c = a + b;
c = a.operator+ (b)

重寫操作符兩種形式: 重寫為成員函數(shù)(上)、重寫為非成員函數(shù)(下)

// non-member operator overloads
#include <iostream>
using namespace std;

class CVector {
  public:
    int x,y;
    CVector () {}
    CVector (int a, int b) : x(a), y(b) {}
};


CVector operator+ (const CVector& lhs, const CVector& rhs) {
  CVector temp;
  temp.x = lhs.x + rhs.x;
  temp.y = lhs.y + rhs.y;
  return temp;
}

int main () {
  CVector foo (3,1);
  CVector bar (1,2);
  CVector result;
  result = foo + bar;
  cout << result.x << ',' << result.y << '\n';
  return 0;
}

this 關(guān)鍵字

this 關(guān)鍵字用來指示調(diào)用函數(shù)的當(dāng)前對(duì)象的地址(當(dāng)前對(duì)象的指針)。

// example on this
#include <iostream>
using namespace std;

class Dummy {
  public:
    bool isitme (Dummy& param);
};

bool Dummy::isitme (Dummy& param)
{
  if (&param == this) return true;
  else return false;
}

int main () {
  Dummy a;
  Dummy* b = &a;
  if ( b->isitme(a) )
    cout << "yes, &a is b\n";
  return 0;
}

this 也常用于通過引用返回對(duì)象的賦值運(yùn)算函數(shù)中,如下(重寫了笛卡爾向量的賦值操作)

CVector& CVector::operator= (const CVector& param)
{
  x=param.x;
  y=param.y;
  return *this;
}

事實(shí)上,此 operator= 函數(shù)已經(jīng)非常接近系統(tǒng)隱式生成的 operator= 代碼了。

類中的靜態(tài)成員

class 可以包含靜態(tài)成員(成員變量 、函數(shù))。關(guān)鍵字: static

靜態(tài)成員變量全局只有一份內(nèi)存,就像類的變量,可以直接用類名訪問,所有的類對(duì)象訪問其靜態(tài)變量都是同一份內(nèi)存。正是由于靜態(tài)變量全局只有一份內(nèi)存,所以其不能在class內(nèi)部進(jìn)行初始化,防止在創(chuàng)建新對(duì)象的時(shí)候多次初始化賦值。需要在class外部初始化一次即可。

// static members in classes
#include <iostream>
using namespace std;

class Dummy {
  public:
    static int n;
    Dummy () { n++; };
};

int Dummy::n=0;  // 靜態(tài)變量,在class 初始化一次即可

int main () {
  Dummy a;
  Dummy b[5];
  cout << a.n << '\n';
  Dummy * c = new Dummy;
  cout << Dummy::n << '\n';
  delete c;
  return 0;
}

// -------------,事實(shí)證明每次構(gòu)造新對(duì)象,內(nèi)部全局變量都是同一份內(nèi)存
6
7

靜態(tài)函數(shù): 同樣可以看做是外部函數(shù),其調(diào)用方式:直接用類名調(diào)用,函數(shù)內(nèi)部不能訪問類實(shí)例變量,也不能使用功能this指針。

常量類型的成員函數(shù)

當(dāng)一個(gè)類的對(duì)象被聲明為常量: const MyClass myobject; 該對(duì)象就可以當(dāng)做一個(gè)只讀對(duì)象,對(duì)于其所有成員(實(shí)例變量/函數(shù))都會(huì)被限制成只讀。但是類的構(gòu)造器函數(shù)是正常調(diào)用的。

// constructor on const object
#include <iostream>
using namespace std;

class MyClass {
  public:
    int x;
    MyClass(int val) : x(val) {}
    int get() {return x;}
};

int main() {
  const MyClass foo(10);
// foo.x = 20;            // not valid: x cannot be modified
  cout << foo.x << '\n';  // ok: data member x can be read
  return 0;
}

// ------------
10

被修飾為常量的對(duì)象,可以調(diào)用的函數(shù),也只能是被標(biāo)記了常量函數(shù)的。格式為在函數(shù)名之后,方法體之前添加 const 修飾符,舉例如下:

int get() const {return x;}        // const member function
const int& get() {return x;}       // member function returning a const&
const int& get() const {return x;} // const member function returning a const&

常量對(duì)象: 我們很少這樣定義,主要使用場(chǎng)景為函數(shù)的參數(shù)
常量函數(shù): 函數(shù)是否為常量會(huì)被重載為不同函數(shù),系統(tǒng)會(huì)根據(jù)對(duì)象是否為常量訪問到正確的函數(shù)

// overloading members on constness
#include <iostream>
using namespace std;

class MyClass {
    int x;
  public:
    MyClass(int val) : x(val) {}
    const int& get() const {return x;}
    int& get() {return x;}
};

int main() {
  MyClass foo (10);
  const MyClass bar (20);
  foo.get() = 15;         // ok: get() returns int&
// bar.get() = 25;        // not valid: get() returns const int&
  cout << foo.get() << '\n';
  cout << bar.get() << '\n';

  return 0;
}

// ----------------
15
20

類模板 - Class templates

同函數(shù)模板,類也可以使用模板,模板類允許類內(nèi)部使用模板參數(shù),模板格式template <class T>;

模板類的成員函數(shù),如果在類外部實(shí)現(xiàn),那么函數(shù)也需要聲明template <...> 前綴。

// class templates
#include <iostream>
using namespace std;

template <class T>
class mypair {
    T a, b;
  public:
    mypair (T first, T second)
      {a=first; b=second;}
    T getmax ();
};

template <class T>
T mypair<T>::getmax ()  // 注意函數(shù)名中<T>,它標(biāo)明了模板函數(shù)和模板類的參數(shù)。
{
  T retval;
  retval = a>b? a : b;
  return retval;
}

int main () {
  mypair <int> myobject (100, 75);
  cout << myobject.getmax();
  return 0;
} 

// -----------
100

如上: 使用模板類保存一對(duì) T 類型元素,構(gòu)造函數(shù)定義在內(nèi)部,外部提供了一個(gè)獲取最大值的函數(shù),外部實(shí)現(xiàn)需要添加 template <class T> 函數(shù)前綴。

專業(yè)化模板類

對(duì)于模板函數(shù),如果傳入某種特定類型,實(shí)現(xiàn)了特定處理,這就叫做專業(yè)化模板類。同通用模板區(qū)別如下

template <class T> class mycontainer { ... };   // 通用模板
template <> class mycontainer <char> { ... };   // 專業(yè)模板

注意: 兩種模板函數(shù),沒有繼承關(guān)系. 如下代碼中是完全兩個(gè)類。

// template specialization
#include <iostream>
using namespace std;

// class template:
template <class T>
class mycontainer {
    T element;
  public:
    mycontainer (T arg) {element=arg;}
    T increase () {return ++element;}
};

// class template specialization:
template <>
class mycontainer <char> {
    char element;
  public:
    mycontainer (char arg) {element=arg;}
    char uppercase ()
    {
      if ((element>='a')&&(element<='z'))
      element+='A'-'a';
      return element;
    }
};

int main () {
  mycontainer<int> myint (7);
  mycontainer<char> mychar ('j');
  cout << myint.increase() << endl;
  cout << mychar.uppercase() << endl;
  return 0;
}

// ------
8
J

類的特殊成員函數(shù)

Class 中有六種特殊成員函數(shù),如下

成員函數(shù) 形式: C 為類 隱式生成條件 默認(rèn)操作
默認(rèn)構(gòu)造器: Default constructor C::C(); 無其他構(gòu)造器
析構(gòu)函數(shù): Destructor C::~C(); 析構(gòu)函數(shù)
拷貝構(gòu)造器: Copy constructor C::C (const C&); 無移動(dòng)構(gòu)造和無移動(dòng)賦值 copy所有成員
拷貝賦值: Copy assignment C& operator= (const C&); 無移動(dòng)構(gòu)造和無移動(dòng)賦值 copy所有成員
移動(dòng)構(gòu)造器: Move constructor C::C (C&&); 無析構(gòu)函數(shù)、無復(fù)制構(gòu)造、無復(fù)制和移動(dòng)賦值(即僅有構(gòu)造函數(shù)) 移動(dòng)所有成員
移動(dòng)賦值: Move assignment C& operator= (C&&); 無析構(gòu)函數(shù)、無復(fù)制構(gòu)造、無復(fù)制和移動(dòng)賦值(即僅有構(gòu)造函數(shù)) 移動(dòng)所有成員

默認(rèn)構(gòu)造器 與 自定義構(gòu)造器

如果定義一個(gè)類,類中沒有聲明構(gòu)造器,編譯器會(huì)隱式生成一個(gè)默認(rèn)構(gòu)造器,默認(rèn)構(gòu)造器無參數(shù)。
如果有自定義的構(gòu)造器,則不會(huì)隱式生成默認(rèn)構(gòu)造器,所以通常如果寫構(gòu)造器就要寫兩種:默認(rèn)和自定義

析構(gòu)函數(shù)
析構(gòu)函數(shù): 內(nèi)部主要釋放對(duì)象管理內(nèi)存的成員。如對(duì)象內(nèi)部有其他對(duì)象。

拷貝構(gòu)造器
指的是使用該類對(duì)象作為參數(shù)的構(gòu)造函數(shù)。 如B b = a;

拷貝賦值
重載等號(hào) = , 使用該類對(duì)象賦值給已定義對(duì)象。 如b = a;

移動(dòng)構(gòu)造器
構(gòu)造對(duì)象使用函數(shù)返回值的方式,如 B b = B(); B() 是一個(gè)返回值為 B& 的函數(shù)

移動(dòng)賦值函數(shù)
移動(dòng)賦值,類似拷貝賦值、右側(cè)參數(shù)為未使用對(duì)象

// destructors
#include <iostream>
#include <string>
using namespace std;

class Example4 {
    string* ptr;
  public:
    // constructors:
    Example4() : ptr(new string) {}
    Example4 (const string& str) : ptr(new string(str)) {}
    // destructor:
    ~Example4 () {delete ptr;}
    
    // copy constructor: 深拷貝,隱式生成的淺拷貝
    Example4 (const Example4& x) : ptr(new string(x.content())) {}
    // copy assignment: 
    Example4& operator= (const Example4& x) {
      *ptr = x.content();
      return *this;
    }
    
    // move constructor
    Example4 (Example4&& x) : ptr(x.ptr) {x.ptr=nullptr;}
    // move assignment
    Example4& operator= (Example4&& x) {
      delete ptr; 
      ptr = x.ptr;
      x.ptr=nullptr;
      return *this;
    }
    // access content:
    const string& content() const {return *ptr;}
};

int main () {
  Example4 foo;
  Example4 bar ("Example");
  return 0;
}

看一下平常使用的賦值其本質(zhì)的操作如下:

MyClass fn();            // function returning a MyClass object
MyClass foo;             // default constructor
MyClass bar = foo;       // copy constructor
MyClass baz = fn();      // move constructor
foo = bar;               // copy assignment
baz = MyClass();         // move assignment 

default 和 delete 關(guān)鍵字

C++ 有 default 和 delete 關(guān)鍵字來告訴編譯器使用還是刪除上述特殊函數(shù)。語法: 在函數(shù)聲明后面添加 default / delete 關(guān)鍵字即可

struct Test {
    // 使用默認(rèn)構(gòu)造函數(shù)
    Test() = default;
    // 刪除復(fù)制賦值運(yùn)算符
    Test& operator=(const Test& test) = delete;
    // 使用默認(rèn)析構(gòu)函數(shù)
    ~Test() = default;
};

朋友關(guān)系 和 繼承關(guān)系

Class 中成員默認(rèn)訪問級(jí)別為 private。 意味著在此類之外無法訪問類成員。

朋友: 是用 friend 關(guān)鍵字聲明的類或函數(shù)。朋友函數(shù)能在類外訪問其成員。但是朋友的朋友不具備此能力。

原則就是: 在類中定義的 friend 函數(shù)/類 可以在外部訪問類內(nèi)部成員。誰被friend修飾,誰有此權(quán)限

朋友函數(shù)示例:

// friend functions
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle() {}
    Rectangle (int x, int y) : width(x), height(y) {}
    int area() {return width * height;}
    friend Rectangle duplicate (const Rectangle&);
};

// 此函數(shù)為朋友函數(shù),可以訪問參數(shù)類內(nèi)部成員
Rectangle duplicate (const Rectangle& param)
{
  Rectangle res;
  res.width = param.width*2;
  res.height = param.height*2;
  return res;
}

int main () {
  Rectangle foo;
  Rectangle bar (2,3);
  foo = duplicate (bar);
  cout << foo.area() << '\n';
  return 0;
}

朋友類示例:

// friend class
#include <iostream>
using namespace std;

// 聲明存在該類,后面都可以使用該類名
class Square;

class Rectangle {
    int width, height;
  public:
    int area ()
      {return (width * height);}
    void convert (Square a);
};

class Square {
  // Rectangle 被聲明為朋友類,代表 Rectangle 內(nèi)部可以訪問當(dāng)前類的私有成員
  friend class Rectangle;
  private:
    int side;
  public:
    Square (int a) : side(a) {}
};

void Rectangle::convert (Square a) {
  width = a.side;
  height = a.side;
}
  
int main () {
  Rectangle rect;
  Square sqr (4);
  rect.convert(sqr);
  cout << rect.area();
  return 0;
}

繼承關(guān)系

類之間可以繼承,形成基類和子類的關(guān)系。
子類會(huì)繼承父類的所有的成員。
子類可以自己定義自己新的成員、可以自定義實(shí)現(xiàn)父類的函數(shù)。

語法:

class derived_class_name: public base_class_name
{ /*...*/ };

DEMO

// derived classes
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b;}
 };

class Rectangle: public Polygon {
  public:
    int area ()
      { return width * height; }
 };

class Triangle: public Polygon {
  public:
    int area ()
      { return width * height / 2; }
  };
  
int main () {
  Rectangle rect;
  Triangle trgl;
  rect.set_values (4,5);
  trgl.set_values (4,5);
  cout << rect.area() << '\n';
  cout << trgl.area() << '\n';
  return 0;
}

// --------
20
10

Rectangle 和 Triangle 分別繼承基類 Polygon. 其內(nèi)部成員 width, height, set_values 也都繼承了。

繼承語法中冒號(hào)后的 public 限制了繼承時(shí)候?qū)Ω割惓蓡T的訪問級(jí)別。建議使用public,不同關(guān)鍵字對(duì)子類繼承的成員訪問權(quán)限如下:

Access public protected private
基類的對(duì)象內(nèi)部 yes yes Yes
子類對(duì)象內(nèi)部 yes yes no
外界(其他函數(shù)、類) yes no no

繼承語法三個(gè)關(guān)鍵字: public、protected、private。作用如下
public : 子類完整按照父類的成員本身的訪問繼承
protected : 子類繼承的成員訪問級(jí)別為 public 的會(huì)降級(jí)為 protected
private : 子類繼承的成員都會(huì)降級(jí)為 private

繼承關(guān)系繼承不到的

子類雖然能繼承父類成員,但是以下幾種情況是無法繼承的

  • 父類的構(gòu)造函數(shù)和析構(gòu)函數(shù)
  • 其分配運(yùn)算符成員(operator=)
  • 父類內(nèi)的朋友成員
  • 父類內(nèi)部 private 訪問屬性的成員

子類的構(gòu)造方法會(huì)調(diào)用父類的默認(rèn)構(gòu)造器。也可以通過以下語法調(diào)用指定的父類構(gòu)造器。
指定調(diào)用父類構(gòu)造函數(shù)derived_constructor_name (parameters) : base_constructor_name (parameters) {...}

// constructors and derived classes
#include <iostream>
using namespace std;

class Mother {
  public:
    Mother ()
      { cout << "Mother: no parameters\n"; }
    Mother (int a)
      { cout << "Mother: int parameter\n"; }
};

class Daughter : public Mother {
  public:
    // 子類構(gòu)造函數(shù)沒有指定特定父類構(gòu)造器: 調(diào)用父類默認(rèn)構(gòu)造器
    Daughter (int a)
      { cout << "Daughter: int parameter\n\n"; }
};

class Son : public Mother {
  public:
    // 指定父類構(gòu)造器: 會(huì)調(diào)用父類指定構(gòu)造器
    Son (int a) : Mother (a)
      { cout << "Son: int parameter\n\n"; }
};

int main () {
  Daughter kelly(0);
  Son bud(0);
  
  return 0;
}

// -----------
Mother: no parameters
Daughter: int parameter

Mother: int parameter
Son: int parameter

類的多繼承

C++ 中類的繼承是可以多繼承: 即同時(shí)有多個(gè)父類。語法即在繼承的冒號(hào)之后用分號(hào)列舉父類列表。一個(gè)完整的例子如下:

// multiple inheritance
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    Polygon (int a, int b) : width(a), height(b) {}
};

class Output {
  public:
    static void print (int i);
};

void Output::print (int i) {
  cout << i << '\n';
}

class Rectangle: public Polygon, public Output {
  public:
    Rectangle (int a, int b) : Polygon(a,b) {}
    int area ()
      { return width*height; }
};

class Triangle: public Polygon, public Output {
  public:
    Triangle (int a, int b) : Polygon(a,b) {}
    int area ()
      { return width*height/2; }
};
  
int main () {
  Rectangle rect (4,5);
  Triangle trgl (4,5);
  rect.print (rect.area());
  Triangle::print (trgl.area());
  return 0;
}

// -----------
20
10 

C++ 中類的多態(tài)

多態(tài): 即父類指針指向子類對(duì)象,指針調(diào)用對(duì)象方法的時(shí)候、編譯器能找到正確的對(duì)象類型并正確調(diào)用到真實(shí)子類的對(duì)象方法。

C++ 中的多態(tài)有一些特殊的關(guān)鍵字: virtual。
基類定義的成員是通用成員,子類繼承后一般會(huì)進(jìn)行功能擴(kuò)展。指向基類的指針無法直接訪問子類成員。為了使基類更加通用,可以在基類中使用 virtual 聲明子類的成員。

virtual 作用即定義基類中可以訪問的子類內(nèi)容。

// virtual members
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b; }
    virtual int area ()
      { return 0; }
};

class Rectangle: public Polygon {
  public:
    int area ()
      { return width * height; }
};

class Triangle: public Polygon {
  public:
    int area ()
      { return (width * height / 2); }
};

int main () {
  Rectangle rect;
  Triangle trgl;
  Polygon poly;
  Polygon * ppoly1 = &rect;
  Polygon * ppoly2 = &trgl;
  Polygon * ppoly3 = &poly;
  ppoly1->set_values (4,5);
  ppoly2->set_values (4,5);
  ppoly3->set_values (4,5);
  cout << ppoly1->area() << '\n';
  cout << ppoly2->area() << '\n';
  cout << ppoly3->area() << '\n';
  return 0;
}

// -----------
20
10
0

如上: 基類中 area() 成員是 virtual 修飾的,所以指向基類指針可以直接調(diào)用此方法。否則 area() 方法只能在子類指針的訪問。

抽象基類

C++ 中提供抽象類的概念,其只能做為基類。類似上面示例,基類中提供 virtual 成員,語法為成員函數(shù)聲明的定義直接 =0;

抽象類無法聲明自己對(duì)象,但是可以聲明指向該類型的指針。

// abstract class CPolygon
class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b; }
    // 注意: 只要包含一個(gè)可見成員,該類就成為抽象類
    virtual int area () =0;
    // 抽象基類中,其他成員可以使用 this關(guān)鍵字訪問可見成員。(系統(tǒng)會(huì)訪問到真實(shí)子類成員)
    void printarea()
      { cout << this->area() << '\n'; }
};

正是由于可見成員和抽象類構(gòu)成了 C++ 的多態(tài)特性,下面是一個(gè)完整包含動(dòng)態(tài)內(nèi)存分配類構(gòu)造初始化器、多態(tài) 的示例

// dynamic allocation and polymorphism
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    Polygon (int a, int b) : width(a), height(b) {}
    virtual int area (void) =0;
    void printarea()
      { cout << this->area() << '\n'; }
};

class Rectangle: public Polygon {
  public:
    Rectangle(int a,int b) : Polygon(a,b) {}
    int area()
      { return width*height; }
};

class Triangle: public Polygon {
  public:
    Triangle(int a,int b) : Polygon(a,b) {}
    int area()
      { return width*height/2; }
};

int main () {
  Polygon * ppoly1 = new Rectangle (4,5);   // 直接將子類對(duì)象賦值給父類指針(多態(tài))、動(dòng)態(tài)內(nèi)存分配
  Polygon * ppoly2 = new Triangle (4,5);
  ppoly1->printarea();
  ppoly2->printarea();
  delete ppoly1;
  delete ppoly2;
  return 0;
}

end

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容