C++ 總結(jié) (二、面向?qū)ο?
本文總結(jié)面向?qū)ο蟛糠帧?/p>
Class 類
類 是由結(jié)構(gòu)體擴(kuò)展而來,內(nèi)部成員包含 實(shí)例變量 和 函數(shù)
對(duì)象 是類實(shí)例,類是它的類型,對(duì)象是變量。
類成員(實(shí)例變量和函數(shù))有三種訪問權(quán)限: private、protected、public
- 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 (¶m == 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 = ▭
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;
}