C++面向?qū)ο?/h2>

C++類(lèi)和對(duì)象

C++ 在 C 語(yǔ)言的基礎(chǔ)上增加了面向?qū)ο缶幊蹋珻++ 支持面向?qū)ο蟪绦蛟O(shè)計(jì)。類(lèi)是 C++ 的核心特性,通常被稱(chēng)為用戶(hù)定義的類(lèi)型。

類(lèi)用于指定對(duì)象的形式,它包含了數(shù)據(jù)表示法和用于處理數(shù)據(jù)的方法。類(lèi)中的數(shù)據(jù)和方法稱(chēng)為類(lèi)的成員。函數(shù)在一個(gè)類(lèi)中被稱(chēng)為類(lèi)的成員。

C++類(lèi)定義

定義一個(gè)類(lèi),本質(zhì)上是定義一個(gè)數(shù)據(jù)類(lèi)型的藍(lán)圖。這實(shí)際上并沒(méi)有定義任何數(shù)據(jù),但它定義了類(lèi)的名稱(chēng)意味著什么,也就是說(shuō),它定義了類(lèi)的對(duì)象包括了什么,以及可以在這個(gè)對(duì)象上執(zhí)行哪些操作。

類(lèi)定義是以關(guān)鍵字 class 開(kāi)頭,后跟類(lèi)的名稱(chēng)。類(lèi)的主體是包含在一對(duì)花括號(hào)中。類(lèi)定義后必須跟著一個(gè)分號(hào)或一個(gè)聲明列表。例如,我們使用關(guān)鍵字 class 定義 Box 數(shù)據(jù)類(lèi)型,如下所示:

class Box
{
   public:
      double length;   // 盒子的長(zhǎng)度
      double breadth;  // 盒子的寬度
      double height;   // 盒子的高度
};

關(guān)鍵字 public 確定了類(lèi)成員的訪問(wèn)屬性。在類(lèi)對(duì)象作用域內(nèi),公共成員在類(lèi)的外部是可訪問(wèn)的。您也可以指定類(lèi)的成員為 private 或 protected,這個(gè)我們稍后會(huì)進(jìn)行講解。

C++對(duì)象定義

類(lèi)提供了對(duì)象的藍(lán)圖,所以基本上,對(duì)象是根據(jù)類(lèi)來(lái)創(chuàng)建的。聲明類(lèi)的對(duì)象,就像聲明基本類(lèi)型的變量一樣。下面的語(yǔ)句聲明了類(lèi) Box 的兩個(gè)對(duì)象:


Box Box1;          // 聲明 Box1,類(lèi)型為 Box
Box Box2;          // 聲明 Box2,類(lèi)型為 Box

對(duì)象 Box1 和 Box2 都有它們各自的數(shù)據(jù)成員。

訪問(wèn)數(shù)據(jù)成員

類(lèi)的對(duì)象的公共數(shù)據(jù)成員可以使用直接成員訪問(wèn)運(yùn)算符 (.) 來(lái)訪問(wèn)。為了更好地理解這些概念,讓我們嘗試一下下面的實(shí)例:

#include <iostream>
 
using namespace std;
 
class Box
{
   public:
      double length;   // 長(zhǎng)度
      double breadth;  // 寬度
      double height;   // 高度
};
 
int main( )
{
   Box Box1;        // 聲明 Box1,類(lèi)型為 Box
   Box Box2;        // 聲明 Box2,類(lèi)型為 Box
   double volume = 0.0;     // 用于存儲(chǔ)體積
 
   // box 1 詳述
   Box1.height = 5.0; 
   Box1.length = 6.0; 
   Box1.breadth = 7.0;
 
   // box 2 詳述
   Box2.height = 10.0;
   Box2.length = 12.0;
   Box2.breadth = 13.0;
 
   // box 1 的體積
   volume = Box1.height * Box1.length * Box1.breadth;
   cout << "Box1 的體積:" << volume <<endl;
 
   // box 2 的體積
   volume = Box2.height * Box2.length * Box2.breadth;
   cout << "Box2 的體積:" << volume <<endl;
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Box1 的體積:210
Box2 的體積:1560

需要注意的是,私有的成員和受保護(hù)的成員不能使用直接成員訪問(wèn)運(yùn)算符 (.) 來(lái)直接訪問(wèn)。我們將在后續(xù)的教程中學(xué)習(xí)如何訪問(wèn)私有成員和受保護(hù)的成員。

類(lèi)和對(duì)象詳解

類(lèi)成員函數(shù)

類(lèi)的成員函數(shù)是指那些把定義和原型寫(xiě)在類(lèi)定義內(nèi)部的函數(shù),就像類(lèi)定義中的其他變量一樣。類(lèi)成員函數(shù)是類(lèi)的一個(gè)成員,它可以操作類(lèi)的任意對(duì)象,可以訪問(wèn)對(duì)象中的所有成員。

讓我們看看之前定義的類(lèi) Box,現(xiàn)在我們要使用成員函數(shù)來(lái)訪問(wèn)類(lèi)的成員,而不是直接訪問(wèn)這些類(lèi)的成員:

class Box
{
   public:
      double length;         // 長(zhǎng)度
      double breadth;        // 寬度
      double height;         // 高度
      double getVolume(void);// 返回體積
};

成員函數(shù)可以定義在類(lèi)定義內(nèi)部,或者單獨(dú)使用范圍解析運(yùn)算符 :: 來(lái)定義。在類(lèi)定義中定義的成員函數(shù)把函數(shù)聲明為內(nèi)聯(lián)的,即便沒(méi)有使用 inline 標(biāo)識(shí)符。所以您可以按照如下方式定義 Volume() 函數(shù):

class Box
{
   public:
      double length;      // 長(zhǎng)度
      double breadth;     // 寬度
      double height;      // 高度
   
      double getVolume(void)
      {
         return length * breadth * height;
      }
};

您也可以在類(lèi)的外部使用范圍解析運(yùn)算符 :: 定義該函數(shù),如下所示:

double Box::getVolume(void)
{
    return length * breadth * height;
}

在這里,需要強(qiáng)調(diào)一點(diǎn),在 :: 運(yùn)算符之前必須使用類(lèi)名。調(diào)用成員函數(shù)是在對(duì)象上使用點(diǎn)運(yùn)算符(.),這樣它就能操作與該對(duì)象相關(guān)的數(shù)據(jù),如下所示:

Box myBox;          // 創(chuàng)建一個(gè)對(duì)象

myBox.getVolume();  // 調(diào)用該對(duì)象的成員函數(shù)

讓我們使用上面提到的概念來(lái)設(shè)置和獲取類(lèi)中不同的成員的值:

#include <iostream>

using namespace std;

class Box
{
   public:
      double length;         // 長(zhǎng)度
      double breadth;        // 寬度
      double height;         // 高度

      // 成員函數(shù)聲明
      double getVolume(void);
      void setLength( double len );
      void setBreadth( double bre );
      void setHeight( double hei );
};

// 成員函數(shù)定義
double Box::getVolume(void)
{
    return length * breadth * height;
}

void Box::setLength( double len )
{
    length = len;
}

void Box::setBreadth( double bre )
{
    breadth = bre;
}

void Box::setHeight( double hei )
{
    height = hei;
}

// 程序的主函數(shù)
int main( )
{
   Box Box1;                // 聲明 Box1,類(lèi)型為 Box
   Box Box2;                // 聲明 Box2,類(lèi)型為 Box
   double volume = 0.0;     // 用于存儲(chǔ)體積
 
   // box 1 詳述
   Box1.setLength(6.0); 
   Box1.setBreadth(7.0); 
   Box1.setHeight(5.0);

   // box 2 詳述
   Box2.setLength(12.0); 
   Box2.setBreadth(13.0); 
   Box2.setHeight(10.0);

   // box 1 的體積
   volume = Box1.getVolume();
   cout << "Box1 的體積:" << volume <<endl;

   // box 2 的體積
   volume = Box2.getVolume();
   cout << "Box2 的體積:" << volume <<endl;
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Box1 的體積: 210
Box2 的體積: 1560

類(lèi)訪問(wèn)修飾符

數(shù)據(jù)封裝是面向?qū)ο缶幊痰囊粋€(gè)重要特點(diǎn),它防止函數(shù)直接訪問(wèn)類(lèi)類(lèi)型的內(nèi)部成員。類(lèi)成員的訪問(wèn)限制是通過(guò)在類(lèi)主體內(nèi)部對(duì)各個(gè)區(qū)域標(biāo)記 public、private、protected 來(lái)指定的。關(guān)鍵字 public、private、protected 稱(chēng)為訪問(wèn)修飾符。

一個(gè)類(lèi)可以有多個(gè) public、protected 或 private 標(biāo)記區(qū)域。每個(gè)標(biāo)記區(qū)域在下一個(gè)標(biāo)記區(qū)域開(kāi)始之前或者在遇到類(lèi)主體結(jié)束右括號(hào)之前都是有效的。成員和類(lèi)的默認(rèn)訪問(wèn)修飾符是 private。

class Base {
 
   public:
 
  // 公有成員
 
   protected:
 
  // 受保護(hù)成員
 
   private:
 
  // 私有成員
 
};

公有(public)成員

公有成員在程序中類(lèi)的外部是可訪問(wèn)的。您可以不使用任何成員函數(shù)來(lái)設(shè)置和獲取公有變量的值,如下所示:

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      double length;
      void setLength( double len );
      double getLength( void );
};
 
// 成員函數(shù)定義
double Line::getLength(void)
{
    return length ;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
// 程序的主函數(shù)
int main( )
{
   Line line;
 
   // 設(shè)置長(zhǎng)度
   line.setLength(6.0); 
   cout << "Length of line : " << line.getLength() <<endl;
 
   // 不使用成員函數(shù)設(shè)置長(zhǎng)度
   line.length = 10.0; // OK: 因?yàn)?length 是公有的
   cout << "Length of line : " << line.length <<endl;
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Length of line : 6
Length of line : 10
私有(private)成員

私有成員變量或函數(shù)在類(lèi)的外部是不可訪問(wèn)的,甚至是不可查看的。只有類(lèi)和友元函數(shù)可以訪問(wèn)私有成員。

默認(rèn)情況下,類(lèi)的所有成員都是私有的。例如在下面的類(lèi)中,width 是一個(gè)私有成員,這意味著,如果您沒(méi)有使用任何訪問(wèn)修飾符,類(lèi)的成員將被假定為私有成員:

class Box
{
   double width;
   public:
      double length;
      void setWidth( double wid );
      double getWidth( void );
};

實(shí)際操作中,我們一般會(huì)在私有區(qū)域定義數(shù)據(jù),在公有區(qū)域定義相關(guān)的函數(shù),以便在類(lèi)的外部也可以調(diào)用這些函數(shù),如下所示:

#include <iostream>
 
using namespace std;
 
class Box
{
   public:
      double length;
      void setWidth( double wid );
      double getWidth( void );
 
   private:
      double width;
};
 
// 成員函數(shù)定義
double Box::getWidth(void)
{
    return width ;
}
 
void Box::setWidth( double wid )
{
    width = wid;
}
 
// 程序的主函數(shù)
int main( )
{
   Box box;
 
   // 不使用成員函數(shù)設(shè)置長(zhǎng)度
   box.length = 10.0; // OK: 因?yàn)?length 是公有的
   cout << "Length of box : " << box.length <<endl;
 
   // 不使用成員函數(shù)設(shè)置寬度
   // box.width = 10.0; // Error: 因?yàn)?width 是私有的
   box.setWidth(10.0);  // 使用成員函數(shù)設(shè)置寬度
   cout << "Width of box : " << box.getWidth() <<endl;
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Length of box : 10
Width of box : 10
保護(hù)(protected)成員

保護(hù)成員變量或函數(shù)與私有成員十分相似,但有一點(diǎn)不同,保護(hù)成員在派生類(lèi)(即子類(lèi))中是可訪問(wèn)的。

在下一個(gè)章節(jié)中,您將學(xué)習(xí)到派生類(lèi)和繼承的知識(shí)。現(xiàn)在您可以看到下面的實(shí)例中,我們從父類(lèi) Box 派生了一個(gè)子類(lèi) smallBox。

下面的實(shí)例與前面的實(shí)例類(lèi)似,在這里 width 成員可被派生類(lèi) smallBox 的任何成員函數(shù)訪問(wèn)。

#include <iostream>
using namespace std;
 
class Box
{
   protected:
      double width;
};
 
class SmallBox:Box // SmallBox 是派生類(lèi)
{
   public:
      void setSmallWidth( double wid );
      double getSmallWidth( void );
};
 
// 子類(lèi)的成員函數(shù)
double SmallBox::getSmallWidth(void)
{
    return width ;
}
 
void SmallBox::setSmallWidth( double wid )
{
    width = wid;
}
 
// 程序的主函數(shù)
int main( )
{
   SmallBox box;
 
   // 使用成員函數(shù)設(shè)置寬度
   box.setSmallWidth(5.0);
   cout << "Width of box : "<< box.getSmallWidth() << endl;
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Width of box : 5
繼承中的特點(diǎn)

有public, protected, private三種繼承方式,它們相應(yīng)地改變了基類(lèi)成員的訪問(wèn)屬性。

  1. public 繼承:基類(lèi) public 成員,protected 成員,private 成員的訪問(wèn)屬性在派生類(lèi)中分別變成:public, protected, private

  2. protected 繼承:基類(lèi) public 成員,protected 成員,private 成員的訪問(wèn)屬性在派生類(lèi)中分別變成:protected, protected, private

  3. private 繼承:基類(lèi) public 成員,protected 成員,private 成員的訪問(wèn)屬性在派生類(lèi)中分別變成:private, private, private

但無(wú)論哪種繼承方式,上面兩點(diǎn)都沒(méi)有改變:

  1. private 成員只能被本類(lèi)成員(類(lèi)內(nèi))和友元訪問(wèn),不能被派生類(lèi)訪問(wèn);

  2. protected 成員可以被派生類(lèi)訪問(wèn)。

構(gòu)造函數(shù)和析構(gòu)函數(shù)

類(lèi)的構(gòu)造函數(shù)是類(lèi)的一種特殊的成員函數(shù),它會(huì)在每次創(chuàng)建類(lèi)的新對(duì)象時(shí)執(zhí)行。

構(gòu)造函數(shù)的名稱(chēng)與類(lèi)的名稱(chēng)是完全相同的,并且不會(huì)返回任何類(lèi)型,也不會(huì)返回 void。構(gòu)造函數(shù)可用于為某些成員變量設(shè)置初始值。

下面的實(shí)例有助于更好地理解構(gòu)造函數(shù)的概念:

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line();  // 這是構(gòu)造函數(shù)
 
   private:
      double length;
};
 
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line(void)
{
    cout << "Object is being created" << endl;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
double Line::getLength( void )
{
    return length;
}
// 程序的主函數(shù)
int main( )
{
   Line line;
 
   // 設(shè)置長(zhǎng)度
   line.setLength(6.0); 
   cout << "Length of line : " << line.getLength() <<endl;
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Object is being created
Length of line : 6

帶參數(shù)的構(gòu)造函數(shù)

默認(rèn)的構(gòu)造函數(shù)沒(méi)有任何參數(shù),但如果需要,構(gòu)造函數(shù)也可以帶有參數(shù)。這樣在創(chuàng)建對(duì)象時(shí)就會(huì)給對(duì)象賦初始值,如下面的例子所示:

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line(double len);  // 這是構(gòu)造函數(shù)
 
   private:
      double length;
};
 
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line( double len)
{
    cout << "Object is being created, length = " << len << endl;
    length = len;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
double Line::getLength( void )
{
    return length;
}
// 程序的主函數(shù)
int main( )
{
   Line line(10.0);
 
   // 獲取默認(rèn)設(shè)置的長(zhǎng)度
   cout << "Length of line : " << line.getLength() <<endl;
   // 再次設(shè)置長(zhǎng)度
   line.setLength(6.0); 
   cout << "Length of line : " << line.getLength() <<endl;
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Object is being created, length = 10
Length of line : 10
Length of line : 6
使用初始化列表來(lái)初始化字段

使用初始化列表來(lái)初始化字段:

Line::Line( double len): length(len)
{
    cout << "Object is being created, length = " << len << endl;
}

上面的語(yǔ)法等同于如下語(yǔ)法:

Line::Line( double len)
{
    cout << "Object is being created, length = " << len << endl;
    length = len;
}

假設(shè)有一個(gè)類(lèi) C,具有多個(gè)字段 X、Y、Z 等需要進(jìn)行初始化,同理地,您可以使用上面的語(yǔ)法,只需要在不同的字段使用逗號(hào)進(jìn)行分隔,如下所示:

C::C( double a, double b, double c): X(a), Y(b), Z(c)
{
  ....
}
類(lèi)的析構(gòu)函數(shù)

類(lèi)的析構(gòu)函數(shù)是類(lèi)的一種特殊的成員函數(shù),它會(huì)在每次刪除所創(chuàng)建的對(duì)象時(shí)執(zhí)行。

析構(gòu)函數(shù)的名稱(chēng)與類(lèi)的名稱(chēng)是完全相同的,只是在前面加了個(gè)波浪號(hào)(~)作為前綴,它不會(huì)返回任何值,也不能帶有任何參數(shù)。析構(gòu)函數(shù)有助于在跳出程序(比如關(guān)閉文件、釋放內(nèi)存等)前釋放資源。

下面的實(shí)例有助于更好地理解析構(gòu)函數(shù)的概念:

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line();   // 這是構(gòu)造函數(shù)聲明
      ~Line();  // 這是析構(gòu)函數(shù)聲明
 
   private:
      double length;
};
 
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line(void)
{
    cout << "Object is being created" << endl;
}
Line::~Line(void)
{
    cout << "Object is being deleted" << endl;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
double Line::getLength( void )
{
    return length;
}
// 程序的主函數(shù)
int main( )
{
   Line line;
 
   // 設(shè)置長(zhǎng)度
   line.setLength(6.0); 
   cout << "Length of line : " << line.getLength() <<endl;
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Object is being created
Length of line : 6
Object is being deleted

C++拷貝構(gòu)造函數(shù)

拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),它在創(chuàng)建對(duì)象時(shí),是使用同一類(lèi)中之前創(chuàng)建的對(duì)象來(lái)初始化新創(chuàng)建的對(duì)象??截悩?gòu)造函數(shù)通常用于:

通過(guò)使用另一個(gè)同類(lèi)型的對(duì)象來(lái)初始化新創(chuàng)建的對(duì)象。

復(fù)制對(duì)象把它作為參數(shù)傳遞給函數(shù)。

復(fù)制對(duì)象,并從函數(shù)返回這個(gè)對(duì)象。

如果在類(lèi)中沒(méi)有定義拷貝構(gòu)造函數(shù),編譯器會(huì)自行定義一個(gè)。如果類(lèi)帶有指針變量,并有動(dòng)態(tài)內(nèi)存分配,則它必須有一個(gè)拷貝構(gòu)造函數(shù)??截悩?gòu)造函數(shù)的最常見(jiàn)形式如下:

classname (const classname &obj) {
   // 構(gòu)造函數(shù)的主體
}

在這里,obj 是一個(gè)對(duì)象引用,該對(duì)象是用于初始化另一個(gè)對(duì)象的。

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      int getLength( void );
      Line( int len );             // 簡(jiǎn)單的構(gòu)造函數(shù)
      Line( const Line &obj);      // 拷貝構(gòu)造函數(shù)
      ~Line();                     // 析構(gòu)函數(shù)
 
   private:
      int *ptr;
};
 
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line(int len)
{
    cout << "調(diào)用構(gòu)造函數(shù)" << endl;
    // 為指針?lè)峙鋬?nèi)存
    ptr = new int;
    *ptr = len;
}
 
Line::Line(const Line &obj)
{
    cout << "調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存" << endl;
    ptr = new int;
    *ptr = *obj.ptr; // 拷貝值
}
 
Line::~Line(void)
{
    cout << "釋放內(nèi)存" << endl;
    delete ptr;
}
int Line::getLength( void )
{
    return *ptr;
}
 
void display(Line obj)
{
   cout << "line 大小 : " << obj.getLength() <<endl;
}
 
// 程序的主函數(shù)
int main( )
{
   Line line(10);
 
   display(line);
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

調(diào)用構(gòu)造函數(shù)
調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存
line 大小 : 10
釋放內(nèi)存
釋放內(nèi)存

下面的實(shí)例對(duì)上面的實(shí)例稍作修改,通過(guò)使用已有的同類(lèi)型的對(duì)象來(lái)初始化新創(chuàng)建的對(duì)象:

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      int getLength( void );
      Line( int len );             // 簡(jiǎn)單的構(gòu)造函數(shù)
      Line( const Line &obj);      // 拷貝構(gòu)造函數(shù)
      ~Line();                     // 析構(gòu)函數(shù)
 
   private:
      int *ptr;
};
 
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line(int len)
{
    cout << "調(diào)用構(gòu)造函數(shù)" << endl;
    // 為指針?lè)峙鋬?nèi)存
    ptr = new int;
    *ptr = len;
}
 
Line::Line(const Line &obj)
{
    cout << "調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存" << endl;
    ptr = new int;
    *ptr = *obj.ptr; // 拷貝值
}
 
Line::~Line(void)
{
    cout << "釋放內(nèi)存" << endl;
    delete ptr;
}
int Line::getLength( void )
{
    return *ptr;
}
 
void display(Line obj)
{
   cout << "line 大小 : " << obj.getLength() <<endl;
}
 
// 程序的主函數(shù)
int main( )
{
   Line line1(10);
 
   Line line2 = line1; // 這里也調(diào)用了拷貝構(gòu)造函數(shù)
 
   display(line1);
   display(line2);
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

調(diào)用構(gòu)造函數(shù)
調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存
調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存
line 大小 : 10
釋放內(nèi)存
調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存
line 大小 : 10
釋放內(nèi)存
釋放內(nèi)存
釋放內(nèi)存

C++友元函數(shù)

類(lèi)的友元函數(shù)是定義在類(lèi)外部,但有權(quán)訪問(wèn)類(lèi)的所有私有(private)成員和保護(hù)(protected)成員。盡管友元函數(shù)的原型有在類(lèi)的定義中出現(xiàn)過(guò),但是友元函數(shù)并不是成員函數(shù)。

友元可以是一個(gè)函數(shù),該函數(shù)被稱(chēng)為友元函數(shù);友元也可以是一個(gè)類(lèi),該類(lèi)被稱(chēng)為友元類(lèi),在這種情況下,整個(gè)類(lèi)及其所有成員都是友元。

如果要聲明函數(shù)為一個(gè)類(lèi)的友元,需要在類(lèi)定義中該函數(shù)原型前使用關(guān)鍵字 friend,如下所示:

class Box
{
   double width;
public:
   double length;
   friend void printWidth( Box box );
   void setWidth( double wid );
};

聲明類(lèi) ClassTwo 的所有成員函數(shù)作為類(lèi) ClassOne 的友元,需要在類(lèi) ClassOne 的定義中放置如下聲明:

friend class ClassTwo;

請(qǐng)看下面的程序:

#include <iostream>
 
using namespace std;
 
class Box
{
   double width;
public:
   friend void printWidth( Box box );
   void setWidth( double wid );
};

// 成員函數(shù)定義
void Box::setWidth( double wid )
{
    width = wid;
}

// 請(qǐng)注意:printWidth() 不是任何類(lèi)的成員函數(shù)
void printWidth( Box box )
{
   /* 因?yàn)?printWidth() 是 Box 的友元,它可以直接訪問(wèn)該類(lèi)的任何成員 */
   cout << "Width of box : " << box.width <<endl;
}
 
// 程序的主函數(shù)
int main( )
{
   Box box;
 
   // 使用成員函數(shù)設(shè)置寬度
   box.setWidth(10.0);
   
   // 使用友元函數(shù)輸出寬度
   printWidth( box );
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Width of box : 10

內(nèi)聯(lián)函數(shù)

C++ 內(nèi)聯(lián)函數(shù)是通常與類(lèi)一起使用。如果一個(gè)函數(shù)是內(nèi)聯(lián)的,那么在編譯時(shí),編譯器會(huì)把該函數(shù)的代碼副本放置在每個(gè)調(diào)用該函數(shù)的地方。

對(duì)內(nèi)聯(lián)函數(shù)進(jìn)行任何修改,都需要重新編譯函數(shù)的所有客戶(hù)端,因?yàn)榫幾g器需要重新更換一次所有的代碼,否則將會(huì)繼續(xù)使用舊的函數(shù)。

如果想把一個(gè)函數(shù)定義為內(nèi)聯(lián)函數(shù),則需要在函數(shù)名前面放置關(guān)鍵字 inline,在調(diào)用函數(shù)之前需要對(duì)函數(shù)進(jìn)行定義。如果已定義的函數(shù)多于一行,編譯器會(huì)忽略 inline 限定符。

在類(lèi)定義中的定義的函數(shù)都是內(nèi)聯(lián)函數(shù),即使沒(méi)有使用 inline 說(shuō)明符。

下面是一個(gè)實(shí)例,使用內(nèi)聯(lián)函數(shù)來(lái)返回兩個(gè)數(shù)中的最大值:

#include <iostream>
 
using namespace std;

inline int Max(int x, int y)
{
   return (x > y)? x : y;
}

// 程序的主函數(shù)
int main( )
{

   cout << "Max (20,10): " << Max(20,10) << endl;
   cout << "Max (0,200): " << Max(0,200) << endl;
   cout << "Max (100,1010): " << Max(100,1010) << endl;
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Max (20,10): 20
Max (0,200): 200
Max (100,1010): 1010

C++中的this指針

在 C++ 中,每一個(gè)對(duì)象都能通過(guò) this 指針來(lái)訪問(wèn)自己的地址。this 指針是所有成員函數(shù)的隱含參數(shù)。因此,在成員函數(shù)內(nèi)部,它可以用來(lái)指向調(diào)用對(duì)象。

友元函數(shù)沒(méi)有 this 指針,因?yàn)橛言皇穷?lèi)的成員。只有成員函數(shù)才有 this 指針。

下面的實(shí)例有助于更好地理解 this 指針的概念:

#include <iostream>
 
using namespace std;

class Box
{
   public:
      // 構(gòu)造函數(shù)定義
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      int compare(Box box)
      {
         return this->Volume() > box.Volume();
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2

   if(Box1.compare(Box2))
   {
      cout << "Box2 is smaller than Box1" <<endl;
   }
   else
   {
      cout << "Box2 is equal to or larger than Box1" <<endl;
   }
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Constructor called.
Constructor called.
Box2 is equal to or larger than Box1

C++中指向類(lèi)的指針

一個(gè)指向 C++ 類(lèi)的指針與指向結(jié)構(gòu)的指針類(lèi)似,訪問(wèn)指向類(lèi)的指針的成員,需要使用成員訪問(wèn)運(yùn)算符 ->,就像訪問(wèn)指向結(jié)構(gòu)的指針一樣。與所有的指針一樣,您必須在使用指針之前,對(duì)指針進(jìn)行初始化。

下面的實(shí)例有助于更好地理解指向類(lèi)的指針的概念:

#include <iostream>
 
using namespace std;

class Box
{
   public:
      // 構(gòu)造函數(shù)定義
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2
   Box *ptrBox;                // Declare pointer to a class.

   // 保存第一個(gè)對(duì)象的地址
   ptrBox = &Box1;

   // 現(xiàn)在嘗試使用成員訪問(wèn)運(yùn)算符來(lái)訪問(wèn)成員
   cout << "Volume of Box1: " << ptrBox->Volume() << endl;

   // 保存第二個(gè)對(duì)象的地址
   ptrBox = &Box2;

   // 現(xiàn)在嘗試使用成員訪問(wèn)運(yùn)算符來(lái)訪問(wèn)成員
   cout << "Volume of Box2: " << ptrBox->Volume() << endl;
  
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Constructor called.
Constructor called.
Volume of Box1: 5.94
Volume of Box2: 102

C++類(lèi)的靜態(tài)成員

我們可以使用 static 關(guān)鍵字來(lái)把類(lèi)成員定義為靜態(tài)的。當(dāng)我們聲明類(lèi)的成員為靜態(tài)時(shí),這意味著無(wú)論創(chuàng)建多少個(gè)類(lèi)的對(duì)象,靜態(tài)成員都只有一個(gè)副本。

靜態(tài)成員在類(lèi)的所有對(duì)象中是共享的。如果不存在其他的初始化語(yǔ)句,在創(chuàng)建第一個(gè)對(duì)象時(shí),所有的靜態(tài)數(shù)據(jù)都會(huì)被初始化為零。我們不能把靜態(tài)成員的初始化放置在類(lèi)的定義中,但是可以在類(lèi)的外部通過(guò)使用范圍解析運(yùn)算符 :: 來(lái)重新聲明靜態(tài)變量從而對(duì)它進(jìn)行初始化,如下面的實(shí)例所示。

下面的實(shí)例有助于更好地理解靜成員態(tài)數(shù)據(jù)的概念:

#include <iostream>
 
using namespace std;

class Box
{
   public:
      static int objectCount;
      // 構(gòu)造函數(shù)定義
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // 每次創(chuàng)建對(duì)象時(shí)增加 1
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // 長(zhǎng)度
      double breadth;    // 寬度
      double height;     // 高度
};

// 初始化類(lèi) Box 的靜態(tài)成員
int Box::objectCount = 0;

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // 聲明 box1
   Box Box2(8.5, 6.0, 2.0);    // 聲明 box2

   // 輸出對(duì)象的總數(shù)
   cout << "Total objects: " << Box::objectCount << endl;

   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Constructor called.
Constructor called.
Total objects: 2
靜態(tài)成員函數(shù)

如果把函數(shù)成員聲明為靜態(tài)的,就可以把函數(shù)與類(lèi)的任何特定對(duì)象獨(dú)立開(kāi)來(lái)。靜態(tài)成員函數(shù)即使在類(lèi)對(duì)象不存在的情況下也能被調(diào)用,靜態(tài)函數(shù)只要使用類(lèi)名加范圍解析運(yùn)算符 :: 就可以訪問(wèn)。

靜態(tài)成員函數(shù)只能訪問(wèn)靜態(tài)成員數(shù)據(jù)、其他靜態(tài)成員函數(shù)和類(lèi)外部的其他函數(shù)。

靜態(tài)成員函數(shù)有一個(gè)類(lèi)范圍,他們不能訪問(wèn)類(lèi)的 this 指針。您可以使用靜態(tài)成員函數(shù)來(lái)判斷類(lèi)的某些對(duì)象是否已被創(chuàng)建。

靜態(tài)成員函數(shù)與普通成員函數(shù)的區(qū)別:

靜態(tài)成員函數(shù)沒(méi)有 this 指針,只能訪問(wèn)靜態(tài)成員(包括靜態(tài)成員變量和靜態(tài)成員函數(shù))。普通成員函數(shù)有 this 指針,可以訪問(wèn)類(lèi)中的任意成員;而靜態(tài)成員函數(shù)沒(méi)有 this 指針。

下面的實(shí)例有助于更好地理解靜態(tài)成員函數(shù)的概念:

#include <iostream>
 
using namespace std;

class Box
{
   public:
      static int objectCount;
      // 構(gòu)造函數(shù)定義
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // 每次創(chuàng)建對(duì)象時(shí)增加 1
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      static int getCount()
      {
         return objectCount;
      }
   private:
      double length;     // 長(zhǎng)度
      double breadth;    // 寬度
      double height;     // 高度
};

// 初始化類(lèi) Box 的靜態(tài)成員
int Box::objectCount = 0;

int main(void)
{
  
   // 在創(chuàng)建對(duì)象之前輸出對(duì)象的總數(shù)
   cout << "Inital Stage Count: " << Box::getCount() << endl;

   Box Box1(3.3, 1.2, 1.5);    // 聲明 box1
   Box Box2(8.5, 6.0, 2.0);    // 聲明 box2

   // 在創(chuàng)建對(duì)象之后輸出對(duì)象的總數(shù)
   cout << "Final Stage Count: " << Box::getCount() << endl;

   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2

C++繼承

面向?qū)ο蟪绦蛟O(shè)計(jì)中最重要的一個(gè)概念是繼承。繼承允許我們依據(jù)另一個(gè)類(lèi)來(lái)定義一個(gè)類(lèi),這使得創(chuàng)建和維護(hù)一個(gè)應(yīng)用程序變得更容易。這樣做,也達(dá)到了重用代碼功能和提高執(zhí)行時(shí)間的效果。

當(dāng)創(chuàng)建一個(gè)類(lèi)時(shí),您不需要重新編寫(xiě)新的數(shù)據(jù)成員和成員函數(shù),只需指定新建的類(lèi)繼承了一個(gè)已有的類(lèi)的成員即可。這個(gè)已有的類(lèi)稱(chēng)為基類(lèi),新建的類(lèi)稱(chēng)為派生類(lèi)。

繼承代表了 is a 關(guān)系。例如,哺乳動(dòng)物是動(dòng)物,狗是哺乳動(dòng)物,因此,狗是動(dòng)物,等等。

基類(lèi)和派生類(lèi)

一個(gè)類(lèi)可以派生自多個(gè)類(lèi),這意味著,它可以從多個(gè)基類(lèi)繼承數(shù)據(jù)和函數(shù)。定義一個(gè)派生類(lèi),我們使用一個(gè)類(lèi)派生列表來(lái)指定基類(lèi)。類(lèi)派生列表以一個(gè)或多個(gè)基類(lèi)命名,形式如下:

class derived-class: access-specifier base-class

其中,訪問(wèn)修飾符 access-specifier 是 public、protected 或 private 其中的一個(gè),base-class 是之前定義過(guò)的某個(gè)類(lèi)的名稱(chēng)。如果未使用訪問(wèn)修飾符 access-specifier,則默認(rèn)為 private。

假設(shè)有一個(gè)基類(lèi) Shape,Rectangle 是它的派生類(lèi),如下所示:

#include <iostream>
 
using namespace std;
 
// 基類(lèi)
class Shape 
{
   public:
      void setWidth(int w)
      {
         width = w;
      }
      void setHeight(int h)
      {
         height = h;
      }
   protected:
      int width;
      int height;
};
 
// 派生類(lèi)
class Rectangle: public Shape
{
   public:
      int getArea()
      { 
         return (width * height); 
      }
};
 
int main(void)
{
   Rectangle Rect;
 
   Rect.setWidth(5);
   Rect.setHeight(7);
 
   // 輸出對(duì)象的面積
   cout << "Total area: " << Rect.getArea() << endl;
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Total area: 35

訪問(wèn)控制和繼承

派生類(lèi)可以訪問(wèn)基類(lèi)中所有的非私有成員。因此基類(lèi)成員如果不想被派生類(lèi)的成員函數(shù)訪問(wèn),則應(yīng)在基類(lèi)中聲明為 private。

我們可以根據(jù)訪問(wèn)權(quán)限總結(jié)出不同的訪問(wèn)類(lèi)型,如下所示:

訪問(wèn) public protected private
同一個(gè)類(lèi) yes yes yes
派生類(lèi) yes yes no
外部的類(lèi) yes no no

一個(gè)派生類(lèi)繼承了所有的基類(lèi)方法,但下列情況除外:

  1. 基類(lèi)的構(gòu)造函數(shù)、析構(gòu)函數(shù)和拷貝構(gòu)造函數(shù)。
  2. 基類(lèi)的重載運(yùn)算符。
  3. 基類(lèi)的友元函數(shù)。

繼承類(lèi)型

當(dāng)一個(gè)類(lèi)派生自基類(lèi),該基類(lèi)可以被繼承為 public、protected 或 private 幾種類(lèi)型。繼承類(lèi)型是通過(guò)上面講解的訪問(wèn)修飾符 access-specifier 來(lái)指定的。

我們幾乎不使用 protected 或 private 繼承,通常使用 public 繼承。當(dāng)使用不同類(lèi)型的繼承時(shí),遵循以下幾個(gè)規(guī)則:

  1. 公有繼承(public):當(dāng)一個(gè)類(lèi)派生自公有基類(lèi)時(shí),基類(lèi)的公有成員也是派生類(lèi)的公有成員,基類(lèi)的保護(hù)成員也是派生類(lèi)的保護(hù)成員,基類(lèi)的私有成員不能直接被派生類(lèi)訪問(wèn),但是可以通過(guò)調(diào)用基類(lèi)的公有和保護(hù)成員來(lái)訪問(wèn)。
  2. 保護(hù)繼承(protected): 當(dāng)一個(gè)類(lèi)派生自保護(hù)基類(lèi)時(shí),基類(lèi)的公有和保護(hù)成員將成為派生類(lèi)的保護(hù)成員。
  3. 私有繼承(private):當(dāng)一個(gè)類(lèi)派生自私有基類(lèi)時(shí),基類(lèi)的公有和保護(hù)成員將成為派生類(lèi)的私有成員。

多繼承

多繼承即一個(gè)子類(lèi)可以有多個(gè)父類(lèi),它繼承了多個(gè)父類(lèi)的特性。

C++ 類(lèi)可以從多個(gè)類(lèi)繼承成員,語(yǔ)法如下:


class <派生類(lèi)名>:<繼承方式1><基類(lèi)名1>,<繼承方式2><基類(lèi)名2>,…
{
<派生類(lèi)類(lèi)體>
};

其中,訪問(wèn)修飾符繼承方式是 public、protected 或 private 其中的一個(gè),用來(lái)修飾每個(gè)基類(lèi),各個(gè)基類(lèi)之間用逗號(hào)分隔,如上所示?,F(xiàn)在讓我們一起看看下面的實(shí)例:

#include <iostream>
 
using namespace std;
 
// 基類(lèi) Shape
class Shape 
{
   public:
      void setWidth(int w)
      {
         width = w;
      }
      void setHeight(int h)
      {
         height = h;
      }
   protected:
      int width;
      int height;
};
 
// 基類(lèi) PaintCost
class PaintCost 
{
   public:
      int getCost(int area)
      {
         return area * 70;
      }
};
 
// 派生類(lèi)
class Rectangle: public Shape, public PaintCost
{
   public:
      int getArea()
      { 
         return (width * height); 
      }
};
 
int main(void)
{
   Rectangle Rect;
   int area;
 
   Rect.setWidth(5);
   Rect.setHeight(7);
 
   area = Rect.getArea();
   
   // 輸出對(duì)象的面積
   cout << "Total area: " << Rect.getArea() << endl;
 
   // 輸出總花費(fèi)
   cout << "Total paint cost: $" << Rect.getCost(area) << endl;
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Total area: 35
Total paint cost: $2450

C++重載運(yùn)算符和重載函數(shù)

C++ 允許在同一作用域中的某個(gè)函數(shù)和運(yùn)算符指定多個(gè)定義,分別稱(chēng)為函數(shù)重載和運(yùn)算符重載。

重載聲明是指一個(gè)與之前已經(jīng)在該作用域內(nèi)聲明過(guò)的函數(shù)或方法具有相同名稱(chēng)的聲明,但是它們的參數(shù)列表和定義(實(shí)現(xiàn))不相同。

當(dāng)您調(diào)用一個(gè)重載函數(shù)或重載運(yùn)算符時(shí),編譯器通過(guò)把您所使用的參數(shù)類(lèi)型與定義中的參數(shù)類(lèi)型進(jìn)行比較,決定選用最合適的定義。選擇最合適的重載函數(shù)或重載運(yùn)算符的過(guò)程,稱(chēng)為重載決策。

C++中的函數(shù)重載

在同一個(gè)作用域內(nèi),可以聲明幾個(gè)功能類(lèi)似的同名函數(shù),但是這些同名函數(shù)的形式參數(shù)(指參數(shù)的個(gè)數(shù)、類(lèi)型或者順序)必須不同。您不能僅通過(guò)返回類(lèi)型的不同來(lái)重載函數(shù)。

下面的實(shí)例中,同名函數(shù) print() 被用于輸出不同的數(shù)據(jù)類(lèi)型:

#include <iostream>
using namespace std;
 
class printData 
{
   public:
      void print(int i) {
        cout << "整數(shù)為: " << i << endl;
      }
 
      void print(double  f) {
        cout << "浮點(diǎn)數(shù)為: " << f << endl;
      }
 
      void print(string c) {
        cout << "字符串為: " << c << endl;
      }
};
 
int main(void)
{
   printData pd;
 
   // 輸出整數(shù)
   pd.print(5);
   // 輸出浮點(diǎn)數(shù)
   pd.print(500.263);
   // 輸出字符串
   pd.print("Hello C++");
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

整數(shù)為: 5
浮點(diǎn)數(shù)為: 500.263
字符串為: Hello C++

C++中的運(yùn)算符重載

您可以重定義或重載大部分 C++ 內(nèi)置的運(yùn)算符。這樣,您就能使用自定義類(lèi)型的運(yùn)算符。

重載的運(yùn)算符是帶有特殊名稱(chēng)的函數(shù),函數(shù)名是由關(guān)鍵字 operator 和其后要重載的運(yùn)算符符號(hào)構(gòu)成的。與其他函數(shù)一樣,重載運(yùn)算符有一個(gè)返回類(lèi)型和一個(gè)參數(shù)列表。

Box operator+(const Box&);

聲明加法運(yùn)算符用于把兩個(gè) Box 對(duì)象相加,返回最終的 Box 對(duì)象。大多數(shù)的重載運(yùn)算符可被定義為普通的非成員函數(shù)或者被定義為類(lèi)成員函數(shù)。如果我們定義上面的函數(shù)為類(lèi)的非成員函數(shù),那么我們需要為每次操作傳遞兩個(gè)參數(shù),如下所示:

Box operator+(const Box&, const Box&);

下面的實(shí)例使用成員函數(shù)演示了運(yùn)算符重載的概念。在這里,對(duì)象作為參數(shù)進(jìn)行傳遞,對(duì)象的屬性使用 this 運(yùn)算符進(jìn)行訪問(wèn),如下所示:

#include <iostream>
using namespace std;
 
class Box
{
   public:
 
      double getVolume(void)
      {
         return length * breadth * height;
      }
      void setLength( double len )
      {
          length = len;
      }
 
      void setBreadth( double bre )
      {
          breadth = bre;
      }
 
      void setHeight( double hei )
      {
          height = hei;
      }
      // 重載 + 運(yùn)算符,用于把兩個(gè) Box 對(duì)象相加
      Box operator+(const Box& b)
      {
         Box box;
         box.length = this->length + b.length;
         box.breadth = this->breadth + b.breadth;
         box.height = this->height + b.height;
         return box;
      }
   private:
      double length;      // 長(zhǎng)度
      double breadth;     // 寬度
      double height;      // 高度
};
// 程序的主函數(shù)
int main( )
{
   Box Box1;                // 聲明 Box1,類(lèi)型為 Box
   Box Box2;                // 聲明 Box2,類(lèi)型為 Box
   Box Box3;                // 聲明 Box3,類(lèi)型為 Box
   double volume = 0.0;     // 把體積存儲(chǔ)在該變量中
 
   // Box1 詳述
   Box1.setLength(6.0); 
   Box1.setBreadth(7.0); 
   Box1.setHeight(5.0);
 
   // Box2 詳述
   Box2.setLength(12.0); 
   Box2.setBreadth(13.0); 
   Box2.setHeight(10.0);
 
   // Box1 的體積
   volume = Box1.getVolume();
   cout << "Volume of Box1 : " << volume <<endl;
 
   // Box2 的體積
   volume = Box2.getVolume();
   cout << "Volume of Box2 : " << volume <<endl;
 
   // 把兩個(gè)對(duì)象相加,得到 Box3
   Box3 = Box1 + Box2;
 
   // Box3 的體積
   volume = Box3.getVolume();
   cout << "Volume of Box3 : " << volume <<endl;
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400

可重載運(yùn)算符和不可重載運(yùn)算符

下面是可重載的運(yùn)算符列表:

  1. 雙目算術(shù)運(yùn)算符 + (加),-(減),*(乘),/(除),% (取模)
  2. 關(guān)系運(yùn)算符 ==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于)
  3. 邏輯運(yùn)算符 ||(邏輯或),&&(邏輯與),!(邏輯非)
  4. 單目運(yùn)算符 + (正),-(負(fù)),*(指針),&(取地址)
  5. 自增自減運(yùn)算符 ++(自增),--(自減)
  6. 位運(yùn)算符 | (按位或),& (按位與),~(按位取反),^(按位異或),,<< (左移),>>(右移)
  7. 賦值運(yùn)算符 =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=
  8. 空間申請(qǐng)與釋放 new, delete, new[ ] , delete[]
  9. 其他運(yùn)算符 ()(函數(shù)調(diào)用),->(成員訪問(wèn)),->*(成員指針訪問(wèn)),,(逗號(hào)),

下面是不可重載的運(yùn)算符列表:

  1. .:成員訪問(wèn)運(yùn)算符
  2. ., ->:成員指針訪問(wèn)運(yùn)算符
  3. :::域運(yùn)算符
  4. sizeof:長(zhǎng)度運(yùn)算符
  5. ?::條件運(yùn)算符
  6. : 預(yù)處理符號(hào)

運(yùn)算符重載實(shí)例

一元運(yùn)算符重載

一元運(yùn)算符只對(duì)一個(gè)操作數(shù)進(jìn)行操作,下面是一元運(yùn)算符的實(shí)例:

  1. 遞增運(yùn)算符( ++ )和遞減運(yùn)算符( -- )
  2. 一元減運(yùn)算符,即負(fù)號(hào)( - )
  3. 邏輯非運(yùn)算符( ! )
    一元運(yùn)算符通常出現(xiàn)在它們所操作的對(duì)象的左邊,比如 !obj、-obj 和 ++obj,但有時(shí)它們也可以作為后綴,比如 obj++ 或 obj--。

下面的實(shí)例演示了如何重載一元減運(yùn)算符( - )。

#include <iostream>
using namespace std;
 
class Distance
{
   private:
      int feet;             // 0 到無(wú)窮
      int inches;           // 0 到 12
   public:
      // 所需的構(gòu)造函數(shù)
      Distance(){
         feet = 0;
         inches = 0;
      }
      Distance(int f, int i){
         feet = f;
         inches = i;
      }
      // 顯示距離的方法
      void displayDistance()
      {
         cout << "F: " << feet << " I:" << inches <<endl;
      }
      // 重載負(fù)運(yùn)算符( - )
      Distance operator- ()  
      {
         feet = -feet;
         inches = -inches;
         return Distance(feet, inches);
      }
};
int main()
{
   Distance D1(11, 10), D2(-5, 11);
 
   -D1;                     // 取相反數(shù)
   D1.displayDistance();    // 距離 D1
 
   -D2;                     // 取相反數(shù)
   D2.displayDistance();    // 距離 D2
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

F: -11 I:-10
F: 5 I:-11

希望上面的實(shí)例能夠幫您更好地理解一元運(yùn)算符重載的概念,類(lèi)似地,您可以嘗試重載邏輯非運(yùn)算符( ! )。

二元運(yùn)算符重載

二元運(yùn)算符需要兩個(gè)參數(shù),下面是二元運(yùn)算符的實(shí)例。我們平常使用的加運(yùn)算符( + )、減運(yùn)算符( - )、乘運(yùn)算符( * )和除運(yùn)算符( / )都屬于二元運(yùn)算符。就像加(+)運(yùn)算符。

下面的實(shí)例演示了如何重載加運(yùn)算符( + )。類(lèi)似地,您也可以嘗試重載減運(yùn)算符( - )和除運(yùn)算符( / )。

#include <iostream>
using namespace std;
 
class Box
{
   double length;      // 長(zhǎng)度
   double breadth;     // 寬度
   double height;      // 高度
public:
 
   double getVolume(void)
   {
      return length * breadth * height;
   }
   void setLength( double len )
   {
       length = len;
   }
 
   void setBreadth( double bre )
   {
       breadth = bre;
   }
 
   void setHeight( double hei )
   {
       height = hei;
   }
   // 重載 + 運(yùn)算符,用于把兩個(gè) Box 對(duì)象相加
   Box operator+(const Box& b)
   {
      Box box;
      box.length = this->length + b.length;
      box.breadth = this->breadth + b.breadth;
      box.height = this->height + b.height;
      return box;
   }
};
// 程序的主函數(shù)
int main( )
{
   Box Box1;                // 聲明 Box1,類(lèi)型為 Box
   Box Box2;                // 聲明 Box2,類(lèi)型為 Box
   Box Box3;                // 聲明 Box3,類(lèi)型為 Box
   double volume = 0.0;     // 把體積存儲(chǔ)在該變量中
 
   // Box1 詳述
   Box1.setLength(6.0); 
   Box1.setBreadth(7.0); 
   Box1.setHeight(5.0);
 
   // Box2 詳述
   Box2.setLength(12.0); 
   Box2.setBreadth(13.0); 
   Box2.setHeight(10.0);
 
   // Box1 的體積
   volume = Box1.getVolume();
   cout << "Volume of Box1 : " << volume <<endl;
 
   // Box2 的體積
   volume = Box2.getVolume();
   cout << "Volume of Box2 : " << volume <<endl;
 
   // 把兩個(gè)對(duì)象相加,得到 Box3
   Box3 = Box1 + Box2;
 
   // Box3 的體積
   volume = Box3.getVolume();
   cout << "Volume of Box3 : " << volume <<endl;
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400

關(guān)系運(yùn)算符重載

C++ 語(yǔ)言支持各種關(guān)系運(yùn)算符( < 、 > 、 <= 、 >= 、 == 等等),它們可用于比較 C++ 內(nèi)置的數(shù)據(jù)類(lèi)型。

您可以重載任何一個(gè)關(guān)系運(yùn)算符,重載后的關(guān)系運(yùn)算符可用于比較類(lèi)的對(duì)象。

下面的實(shí)例演示了如何重載 < 運(yùn)算符,類(lèi)似地,您也可以嘗試重載其他的關(guān)系運(yùn)算符。

#include <iostream>
using namespace std;
 
class Distance
{
   private:
      int feet;             // 0 到無(wú)窮
      int inches;           // 0 到 12
   public:
      // 所需的構(gòu)造函數(shù)
      Distance(){
         feet = 0;
         inches = 0;
      }
      Distance(int f, int i){
         feet = f;
         inches = i;
      }
      // 顯示距離的方法
      void displayDistance()
      {
         cout << "F: " << feet << " I:" << inches <<endl;
      }
      // 重載負(fù)運(yùn)算符( - )
      Distance operator- ()  
      {
         feet = -feet;
         inches = -inches;
         return Distance(feet, inches);
      }
      // 重載小于運(yùn)算符( < )
      bool operator <(const Distance& d)
      {
         if(feet < d.feet)
         {
            return true;
         }
         if(feet == d.feet && inches < d.inches)
         {
            return true;
         }
         return false;
      }
};
int main()
{
   Distance D1(11, 10), D2(5, 11);
 
   if( D1 < D2 )
   {
      cout << "D1 is less than D2 " << endl;
   }
   else
   {
      cout << "D2 is less than D1 " << endl;
   }
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

D2 is less than D1

輸入和輸出運(yùn)算符重載

C++ 能夠使用流提取運(yùn)算符 >> 和流插入運(yùn)算符 << 來(lái)輸入和輸出內(nèi)置的數(shù)據(jù)類(lèi)型。您可以重載流提取運(yùn)算符和流插入運(yùn)算符來(lái)操作對(duì)象等用戶(hù)自定義的數(shù)據(jù)類(lèi)型。

在這里,有一點(diǎn)很重要,我們需要把運(yùn)算符重載函數(shù)聲明為類(lèi)的友元函數(shù),這樣我們就能不用創(chuàng)建對(duì)象而直接調(diào)用函數(shù)。

下面的實(shí)例演示了如何重載提取運(yùn)算符 >> 和插入運(yùn)算符 <<。

#include <iostream>
using namespace std;
 
class Distance
{
   private:
      int feet;             // 0 到無(wú)窮
      int inches;           // 0 到 12
   public:
      // 所需的構(gòu)造函數(shù)
      Distance(){
         feet = 0;
         inches = 0;
      }
      Distance(int f, int i){
         feet = f;
         inches = i;
      }
      friend ostream &operator<<( ostream &output, 
                                       const Distance &D )
      { 
         output << "F : " << D.feet << " I : " << D.inches;
         return output;            
      }

      friend istream &operator>>( istream  &input, Distance &D )
      { 
         input >> D.feet >> D.inches;
         return input;            
      }
};
int main()
{
   Distance D1(11, 10), D2(5, 11), D3;

   cout << "Enter the value of object : " << endl;
   cin >> D3;
   cout << "First Distance : " << D1 << endl;
   cout << "Second Distance :" << D2 << endl;
   cout << "Third Distance :" << D3 << endl;


   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

$./a.out
Enter the value of object :
70
10
First Distance : F : 11 I : 10
Second Distance :F : 5 I : 11
Third Distance :F : 70 I : 10

++和--運(yùn)算符重載

遞增運(yùn)算符( ++ )和遞減運(yùn)算符( -- )是 C++ 語(yǔ)言中兩個(gè)重要的一元運(yùn)算符。

下面的實(shí)例演示了如何重載遞增運(yùn)算符( ++ ),包括前綴和后綴兩種用法。類(lèi)似地,您也可以嘗試重載遞減運(yùn)算符( -- )。

#include <iostream>
using namespace std;
 
class Time
{
   private:
      int hours;             // 0 到 23
      int minutes;           // 0 到 59
   public:
      // 所需的構(gòu)造函數(shù)
      Time(){
         hours = 0;
         minutes = 0;
      }
      Time(int h, int m){
         hours = h;
         minutes = m;
      }
      // 顯示時(shí)間的方法
      void displayTime()
      {
         cout << "H: " << hours << " M:" << minutes <<endl;
      }
      // 重載前綴遞增運(yùn)算符( ++ )
      Time operator++ ()  
      {
         ++minutes;          // 對(duì)象加 1
         if(minutes >= 60)  
         {
            ++hours;
            minutes -= 60;
         }
         return Time(hours, minutes);
      }
      // 重載后綴遞增運(yùn)算符( ++ )
      Time operator++( int )         
      {
         // 保存原始值
         Time T(hours, minutes);
         // 對(duì)象加 1
         ++minutes;                    
         if(minutes >= 60)
         {
            ++hours;
            minutes -= 60;
         }
         // 返回舊的原始值
         return T; 
      }
};
int main()
{
   Time T1(11, 59), T2(10,40);
 
   ++T1;                    // T1 加 1
   T1.displayTime();        // 顯示 T1
   ++T1;                    // T1 再加 1
   T1.displayTime();        // 顯示 T1
 
   T2++;                    // T2 加 1
   T2.displayTime();        // 顯示 T2
   T2++;                    // T2 再加 1
   T2.displayTime();        // 顯示 T2
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

H: 12 M:0
H: 12 M:1
H: 10 M:41
H: 10 M:42

賦值運(yùn)算符重載

就像其他運(yùn)算符一樣,您可以重載賦值運(yùn)算符( = ),用于創(chuàng)建一個(gè)對(duì)象,比如拷貝構(gòu)造函數(shù)。

下面的實(shí)例演示了如何重載賦值運(yùn)算符。


#include <iostream>
using namespace std;
 
class Distance
{
   private:
      int feet;             // 0 到無(wú)窮
      int inches;           // 0 到 12
   public:
      // 所需的構(gòu)造函數(shù)
      Distance(){
         feet = 0;
         inches = 0;
      }
      Distance(int f, int i){
         feet = f;
         inches = i;
      }
      void operator=(const Distance &D )
      { 
         feet = D.feet;
         inches = D.inches;
      }
      // 顯示距離的方法
      void displayDistance()
      {
         cout << "F: " << feet <<  " I:" <<  inches << endl;
      }
      
};
int main()
{
   Distance D1(11, 10), D2(5, 11);

   cout << "First Distance : "; 
   D1.displayDistance();
   cout << "Second Distance :"; 
   D2.displayDistance();

   // 使用賦值運(yùn)算符
   D1 = D2;
   cout << "First Distance :"; 
   D1.displayDistance();

   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

First Distance : F: 11 I:10
Second Distance :F: 5 I:11
First Distance :F: 5 I:11

函數(shù)調(diào)用運(yùn)算符()重載

函數(shù)調(diào)用運(yùn)算符 () 可以被重載用于類(lèi)的對(duì)象。當(dāng)重載 () 時(shí),您不是創(chuàng)造了一種新的調(diào)用函數(shù)的方式,相反地,這是創(chuàng)建一個(gè)可以傳遞任意數(shù)目參數(shù)的運(yùn)算符函數(shù)。

下面的實(shí)例演示了如何重載函數(shù)調(diào)用運(yùn)算符 ()。

#include <iostream>
using namespace std;
 
class Distance
{
   private:
      int feet;             // 0 到無(wú)窮
      int inches;           // 0 到 12
   public:
      // 所需的構(gòu)造函數(shù)
      Distance(){
         feet = 0;
         inches = 0;
      }
      Distance(int f, int i){
         feet = f;
         inches = i;
      }
      // 重載函數(shù)調(diào)用運(yùn)算符
      Distance operator()(int a, int b, int c)
      {
         Distance D;
         // 進(jìn)行隨機(jī)計(jì)算
         D.feet = a + c + 10;
         D.inches = b + c + 100 ;
         return D;
      }
      // 顯示距離的方法
      void displayDistance()
      {
         cout << "F: " << feet <<  " I:" <<  inches << endl;
      }
      
};
int main()
{
   Distance D1(11, 10), D2;

   cout << "First Distance : "; 
   D1.displayDistance();

   D2 = D1(10, 10, 10); // invoke operator()
   cout << "Second Distance :"; 
   D2.displayDistance();

   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

First Distance : F: 11 I:10
Second Distance :F: 30 I:120

下標(biāo)運(yùn)算符[]重載

下標(biāo)操作符 [] 通常用于訪問(wèn)數(shù)組元素。重載該運(yùn)算符用于增強(qiáng)操作 C++ 數(shù)組的功能。

下面的實(shí)例演示了如何重載下標(biāo)運(yùn)算符 []。

#include <iostream>
using namespace std;
const int SIZE = 10;

class safearay
{
   private:
      int arr[SIZE];
   public:
      safearay() 
      {
         register int i;
         for(i = 0; i < SIZE; i++)
         {
           arr[i] = i;
         }
      }
      int& operator[](int i)
      {
          if( i > SIZE )
          {
              cout << "索引超過(guò)最大值" <<endl; 
              // 返回第一個(gè)元素
              return arr[0];
          }
          return arr[i];
      }
};
int main()
{
   safearay A;

   cout << "A[2] 的值為 : " << A[2] <<endl;
   cout << "A[5] 的值為 : " << A[5]<<endl;
   cout << "A[12] 的值為 : " << A[12]<<endl;

   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

$ g++ -o test test.cpp
$ ./test 
A[2] 的值為 : 2
A[5] 的值為 : 5
A[12] 的值為 : 索引超過(guò)最大值
0

類(lèi)成員訪問(wèn)運(yùn)算符->重載

類(lèi)成員訪問(wèn)運(yùn)算符( -> )可以被重載,但它較為麻煩。它被定義用于為一個(gè)類(lèi)賦予"指針"行為。運(yùn)算符 -> 必須是一個(gè)成員函數(shù)。如果使用了 -> 運(yùn)算符,返回類(lèi)型必須是指針或者是類(lèi)的對(duì)象。

運(yùn)算符 -> 通常與指針引用運(yùn)算符 * 結(jié)合使用,用于實(shí)現(xiàn)"智能指針"的功能。這些指針是行為與正常指針相似的對(duì)象,唯一不同的是,當(dāng)您通過(guò)指針訪問(wèn)對(duì)象時(shí),它們會(huì)執(zhí)行其他的任務(wù)。比如,當(dāng)指針?shù)N毀時(shí),或者當(dāng)指針指向另一個(gè)對(duì)象時(shí),會(huì)自動(dòng)刪除對(duì)象。

間接引用運(yùn)算符 -> 可被定義為一個(gè)一元后綴運(yùn)算符。也就是說(shuō),給出一個(gè)類(lèi):

class Ptr{
   //...
   X * operator->();
};

類(lèi) Ptr 的對(duì)象可用于訪問(wèn)類(lèi) X 的成員,使用方式與指針的用法十分相似。例如:

void f(Ptr p )
{
   p->m = 10 ; // (p.operator->())->m = 10
}

語(yǔ)句 p->m 被解釋為 (p.operator->())->m。同樣地,下面的實(shí)例演示了如何重載類(lèi)成員訪問(wèn)運(yùn)算符 ->。

#include <iostream>
#include <vector>
using namespace std;

// 假設(shè)一個(gè)實(shí)際的類(lèi)
class Obj {
   static int i, j;
public:
   void f() const { cout << i++ << endl; }
   void g() const { cout << j++ << endl; }
};

// 靜態(tài)成員定義
int Obj::i = 10;
int Obj::j = 12;

// 為上面的類(lèi)實(shí)現(xiàn)一個(gè)容器
class ObjContainer {
   vector<Obj*> a;
public:
   void add(Obj* obj)
   { 
      a.push_back(obj);  // 調(diào)用向量的標(biāo)準(zhǔn)方法
   }
   friend class SmartPointer;
};

// 實(shí)現(xiàn)智能指針,用于訪問(wèn)類(lèi) Obj 的成員
class SmartPointer {
   ObjContainer oc;
   int index;
public:
   SmartPointer(ObjContainer& objc)
   { 
       oc = objc;
       index = 0;
   }
   // 返回值表示列表結(jié)束
   bool operator++() // 前綴版本
   { 
     if(index >= oc.a.size()) return false;
     if(oc.a[++index] == 0) return false;
     return true;
   }
   bool operator++(int) // 后綴版本
   { 
      return operator++();
   }
   // 重載運(yùn)算符 ->
   Obj* operator->() const 
   {
     if(!oc.a[index])
     {
        cout << "Zero value";
        return (Obj*)0;
     }
     return oc.a[index];
   }
};

int main() {
   const int sz = 10;
   Obj o[sz];
   ObjContainer oc;
   for(int i = 0; i < sz; i++)
   {
       oc.add(&o[i]);
   }
   SmartPointer sp(oc); // 創(chuàng)建一個(gè)迭代器
   do {
      sp->f(); // 智能指針調(diào)用
      sp->g();
   } while(sp++);
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

10
12
11
13
12
14
13
15
14
16
15
17
16
18
17
19
18
20
19
21

C++多態(tài)

多態(tài)按字面的意思就是多種形態(tài)。當(dāng)類(lèi)之間存在層次結(jié)構(gòu),并且類(lèi)之間是通過(guò)繼承關(guān)聯(lián)時(shí),就會(huì)用到多態(tài)。

C++ 多態(tài)意味著調(diào)用成員函數(shù)時(shí),會(huì)根據(jù)調(diào)用函數(shù)的對(duì)象的類(lèi)型來(lái)執(zhí)行不同的函數(shù)。

下面的實(shí)例中,基類(lèi) Shape 被派生為兩個(gè)類(lèi),如下所示:

#include <iostream> 
using namespace std;
 
class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      int area()
      {
         cout << "Parent class area :" <<endl;
         return 0;
      }
};
class Rectangle: public Shape{
   public:
      Rectangle( int a=0, int b=0):Shape(a, b) { }
      int area ()
      { 
         cout << "Rectangle class area :" <<endl;
         return (width * height); 
      }
};
class Triangle: public Shape{
   public:
      Triangle( int a=0, int b=0):Shape(a, b) { }
      int area ()
      { 
         cout << "Triangle class area :" <<endl;
         return (width * height / 2); 
      }
};
// 程序的主函數(shù)
int main( )
{
   Shape *shape;
   Rectangle rec(10,7);
   Triangle  tri(10,5);
 
   // 存儲(chǔ)矩形的地址
   shape = &rec;
   // 調(diào)用矩形的求面積函數(shù) area
   shape->area();
 
   // 存儲(chǔ)三角形的地址
   shape = &tri;
   // 調(diào)用三角形的求面積函數(shù) area
   shape->area();
   
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Parent class area
Parent class area

導(dǎo)致錯(cuò)誤輸出的原因是,調(diào)用函數(shù) area() 被編譯器設(shè)置為基類(lèi)中的版本,這就是所謂的靜態(tài)多態(tài),或靜態(tài)鏈接 - 函數(shù)調(diào)用在程序執(zhí)行前就準(zhǔn)備好了。有時(shí)候這也被稱(chēng)為早綁定,因?yàn)?area() 函數(shù)在程序編譯期間就已經(jīng)設(shè)置好了。

但現(xiàn)在,讓我們對(duì)程序稍作修改,在 Shape 類(lèi)中,area() 的聲明前放置關(guān)鍵字 virtual,如下所示:

class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      virtual int area()
      {
         cout << "Parent class area :" <<endl;
         return 0;
      }
};

修改后,當(dāng)編譯和執(zhí)行前面的實(shí)例代碼時(shí),它會(huì)產(chǎn)生以下結(jié)果:

Rectangle class area
Triangle class area

此時(shí),編譯器看的是指針的內(nèi)容,而不是它的類(lèi)型。因此,由于 tri 和 rec 類(lèi)的對(duì)象的地址存儲(chǔ)在 *shape 中,所以會(huì)調(diào)用各自的 area() 函數(shù)。

正如您所看到的,每個(gè)子類(lèi)都有一個(gè)函數(shù) area() 的獨(dú)立實(shí)現(xiàn)。這就是多態(tài)的一般使用方式。有了多態(tài),您可以有多個(gè)不同的類(lèi),都帶有同一個(gè)名稱(chēng)但具有不同實(shí)現(xiàn)的函數(shù),函數(shù)的參數(shù)甚至可以是相同的。

虛函數(shù)

虛函數(shù) 是在基類(lèi)中使用關(guān)鍵字 virtual 聲明的函數(shù)。在派生類(lèi)中重新定義基類(lèi)中定義的虛函數(shù)時(shí),會(huì)告訴編譯器不要靜態(tài)鏈接到該函數(shù)。

我們想要的是在程序中任意點(diǎn)可以根據(jù)所調(diào)用的對(duì)象類(lèi)型來(lái)選擇調(diào)用的函數(shù),這種操作被稱(chēng)為動(dòng)態(tài)鏈接,或后期綁定。

純虛函數(shù)

您可能想要在基類(lèi)中定義虛函數(shù),以便在派生類(lèi)中重新定義該函數(shù)更好地適用于對(duì)象,但是您在基類(lèi)中又不能對(duì)虛函數(shù)給出有意義的實(shí)現(xiàn),這個(gè)時(shí)候就會(huì)用到純虛函數(shù)。

我們可以把基類(lèi)中的虛函數(shù) area() 改寫(xiě)如下:

class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      // pure virtual function
      virtual int area() = 0;
};

= 0 告訴編譯器,函數(shù)沒(méi)有主體,上面的虛函數(shù)是純虛函數(shù)。

C++數(shù)據(jù)抽象

數(shù)據(jù)抽象是指,只向外界提供關(guān)鍵信息,并隱藏其后臺(tái)的實(shí)現(xiàn)細(xì)節(jié),即只表現(xiàn)必要的信息而不呈現(xiàn)細(xì)節(jié)。

數(shù)據(jù)抽象是一種依賴(lài)于接口和實(shí)現(xiàn)分離的編程(設(shè)計(jì))技術(shù)。

讓我們舉一個(gè)現(xiàn)實(shí)生活中的真實(shí)例子,比如一臺(tái)電視機(jī),您可以打開(kāi)和關(guān)閉、切換頻道、調(diào)整音量、添加外部組件(如喇叭、錄像機(jī)、DVD 播放器),但是您不知道它的內(nèi)部實(shí)現(xiàn)細(xì)節(jié),也就是說(shuō),您并不知道它是如何通過(guò)纜線接收信號(hào),如何轉(zhuǎn)換信號(hào),并最終顯示在屏幕上。

因此,我們可以說(shuō)電視把它的內(nèi)部實(shí)現(xiàn)和外部接口分離開(kāi)了,您無(wú)需知道它的內(nèi)部實(shí)現(xiàn)原理,直接通過(guò)它的外部接口(比如電源按鈕、遙控器、聲量控制器)就可以操控電視。

現(xiàn)在,讓我們言歸正傳,就 C++ 編程而言,C++ 類(lèi)為數(shù)據(jù)抽象提供了可能。它們向外界提供了大量用于操作對(duì)象數(shù)據(jù)的公共方法,也就是說(shuō),外界實(shí)際上并不清楚類(lèi)的內(nèi)部實(shí)現(xiàn)。

例如,您的程序可以調(diào)用 sort() 函數(shù),而不需要知道函數(shù)中排序數(shù)據(jù)所用到的算法。實(shí)際上,函數(shù)排序的底層實(shí)現(xiàn)會(huì)因庫(kù)的版本不同而有所差異,只要接口不變,函數(shù)調(diào)用就可以照常工作。

在 C++ 中,我們使用類(lèi)來(lái)定義我們自己的抽象數(shù)據(jù)類(lèi)型(ADT)。您可以使用類(lèi) ostream 的 cout 對(duì)象來(lái)輸出數(shù)據(jù)到標(biāo)準(zhǔn)輸出,如下所示:

#include <iostream>
using namespace std;
 
int main( )
{
   cout << "Hello C++" <<endl;
   return 0;
}

在這里,您不需要理解 cout 是如何在用戶(hù)的屏幕上顯示文本。您只需要知道公共接口即可,cout 的底層實(shí)現(xiàn)可以自由改變。

訪問(wèn)標(biāo)簽強(qiáng)制抽象

在 C++ 中,我們使用訪問(wèn)標(biāo)簽來(lái)定義類(lèi)的抽象接口。一個(gè)類(lèi)可以包含零個(gè)或多個(gè)訪問(wèn)標(biāo)簽:

  1. 使用公共標(biāo)簽定義的成員都可以訪問(wèn)該程序的所有部分。一個(gè)類(lèi)型的數(shù)據(jù)抽象視圖是由它的公共成員來(lái)定義的。
  2. 使用私有標(biāo)簽定義的成員無(wú)法訪問(wèn)到使用類(lèi)的代碼。私有部分對(duì)使用類(lèi)型的代碼隱藏了實(shí)現(xiàn)細(xì)節(jié)。
    訪問(wèn)標(biāo)簽出現(xiàn)的頻率沒(méi)有限制。每個(gè)訪問(wèn)標(biāo)簽指定了緊隨其后的成員定義的訪問(wèn)級(jí)別。指定的訪問(wèn)級(jí)別會(huì)一直有效,直到遇到下一個(gè)訪問(wèn)標(biāo)簽或者遇到類(lèi)主體的關(guān)閉右括號(hào)為止。

數(shù)據(jù)抽象的好處

數(shù)據(jù)抽象有兩個(gè)重要的優(yōu)勢(shì):

  1. 類(lèi)的內(nèi)部受到保護(hù),不會(huì)因無(wú)意的用戶(hù)級(jí)錯(cuò)誤導(dǎo)致對(duì)象狀態(tài)受損。
  2. 類(lèi)實(shí)現(xiàn)可能隨著時(shí)間的推移而發(fā)生變化,以便應(yīng)對(duì)不斷變化的需求,或者應(yīng)對(duì)那些要求不改變用戶(hù)級(jí)代碼的錯(cuò)誤報(bào)告。
    如果只在類(lèi)的私有部分定義數(shù)據(jù)成員,編寫(xiě)該類(lèi)的作者就可以隨意更改數(shù)據(jù)。如果實(shí)現(xiàn)發(fā)生改變,則只需要檢查類(lèi)的代碼,看看這個(gè)改變會(huì)導(dǎo)致哪些影響。如果數(shù)據(jù)是公有的,則任何直接訪問(wèn)舊表示形式的數(shù)據(jù)成員的函數(shù)都可能受到影響。

數(shù)據(jù)抽象的實(shí)例

C++ 程序中,任何帶有公有和私有成員的類(lèi)都可以作為數(shù)據(jù)抽象的實(shí)例。請(qǐng)看下面的實(shí)例:

#include <iostream>
using namespace std;
 
class Adder{
   public:
      // 構(gòu)造函數(shù)
      Adder(int i = 0)
      {
        total = i;
      }
      // 對(duì)外的接口
      void addNum(int number)
      {
          total += number;
      }
      // 對(duì)外的接口
      int getTotal()
      {
          return total;
      };
   private:
      // 對(duì)外隱藏的數(shù)據(jù)
      int total;
};
int main( )
{
   Adder a;
   
   a.addNum(10);
   a.addNum(20);
   a.addNum(30);
 
   cout << "Total " << a.getTotal() <<endl;
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Total 60

上面的類(lèi)把數(shù)字相加,并返回總和。公有成員 addNum 和 getTotal 是對(duì)外的接口,用戶(hù)需要知道它們以便使用類(lèi)。私有成員 total 是用戶(hù)不需要了解的,但又是類(lèi)能正常工作所必需的。

設(shè)計(jì)策略

抽象把代碼分離為接口和實(shí)現(xiàn)。所以在設(shè)計(jì)組件時(shí),必須保持接口獨(dú)立于實(shí)現(xiàn),這樣,如果改變底層實(shí)現(xiàn),接口也將保持不變。

在這種情況下,不管任何程序使用接口,接口都不會(huì)受到影響,只需要將最新的實(shí)現(xiàn)重新編譯即可。

C++數(shù)據(jù)封裝

所有的 C++ 程序都有以下兩個(gè)基本要素:

  1. 程序語(yǔ)句(代碼):這是程序中執(zhí)行動(dòng)作的部分,它們被稱(chēng)為函數(shù)。
  2. 程序數(shù)據(jù):數(shù)據(jù)是程序的信息,會(huì)受到程序函數(shù)的影響。

封裝是面向?qū)ο缶幊讨械陌褦?shù)據(jù)和操作數(shù)據(jù)的函數(shù)綁定在一起的一個(gè)概念,這樣能避免受到外界的干擾和誤用,從而確保了安全。數(shù)據(jù)封裝引申出了另一個(gè)重要的 OOP 概念,即數(shù)據(jù)隱藏。

數(shù)據(jù)封裝是一種把數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)捆綁在一起的機(jī)制,數(shù)據(jù)抽象是一種僅向用戶(hù)暴露接口而把具體的實(shí)現(xiàn)細(xì)節(jié)隱藏起來(lái)的機(jī)制。

C++ 通過(guò)創(chuàng)建類(lèi)來(lái)支持封裝和數(shù)據(jù)隱藏(public、protected、private)。我們已經(jīng)知道,類(lèi)包含私有成員(private)、保護(hù)成員(protected)和公有成員(public)成員。默認(rèn)情況下,在類(lèi)中定義的所有項(xiàng)目都是私有的。例如:

class Box
{
   public:
      double getVolume(void)
      {
         return length * breadth * height;
      }
   private:
      double length;      // 長(zhǎng)度
      double breadth;     // 寬度
      double height;      // 高度
};

變量 length、breadth 和 height 都是私有的(private)。這意味著它們只能被 Box 類(lèi)中的其他成員訪問(wèn),而不能被程序中其他部分訪問(wèn)。這是實(shí)現(xiàn)封裝的一種方式。

為了使類(lèi)中的成員變成公有的(即,程序中的其他部分也能訪問(wèn)),必須在這些成員前使用 public 關(guān)鍵字進(jìn)行聲明。所有定義在 public 標(biāo)識(shí)符后邊的變量或函數(shù)可以被程序中所有其他的函數(shù)訪問(wèn)。

把一個(gè)類(lèi)定義為另一個(gè)類(lèi)的友元類(lèi),會(huì)暴露實(shí)現(xiàn)細(xì)節(jié),從而降低了封裝性。理想的做法是盡可能地對(duì)外隱藏每個(gè)類(lèi)的實(shí)現(xiàn)細(xì)節(jié)。

數(shù)據(jù)封裝的實(shí)例

C++ 程序中,任何帶有公有和私有成員的類(lèi)都可以作為數(shù)據(jù)封裝和數(shù)據(jù)抽象的實(shí)例。請(qǐng)看下面的實(shí)例:

#include <iostream>
using namespace std;
 
class Adder{
   public:
      // 構(gòu)造函數(shù)
      Adder(int i = 0)
      {
        total = i;
      }
      // 對(duì)外的接口
      void addNum(int number)
      {
          total += number;
      }
      // 對(duì)外的接口
      int getTotal()
      {
          return total;
      };
   private:
      // 對(duì)外隱藏的數(shù)據(jù)
      int total;
};
int main( )
{
   Adder a;
   
   a.addNum(10);
   a.addNum(20);
   a.addNum(30);
 
   cout << "Total " << a.getTotal() <<endl;
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Total 60

上面的類(lèi)把數(shù)字相加,并返回總和。公有成員 addNum 和 getTotal 是對(duì)外的接口,用戶(hù)需要知道它們以便使用類(lèi)。私有成員 total 是對(duì)外隱藏的,用戶(hù)不需要了解它,但它又是類(lèi)能正常工作所必需的。

設(shè)計(jì)策略

通常情況下,我們都會(huì)設(shè)置類(lèi)成員狀態(tài)為私有(private),除非我們真的需要將其暴露,這樣才能保證良好的封裝性。

這通常應(yīng)用于數(shù)據(jù)成員,但它同樣適用于所有成員,包括虛函數(shù)。

C++接口(抽象類(lèi))

接口描述了類(lèi)的行為和功能,而不需要完成類(lèi)的特定實(shí)現(xiàn)。

C++ 接口是使用抽象類(lèi)來(lái)實(shí)現(xiàn)的,抽象類(lèi)與數(shù)據(jù)抽象互不混淆,數(shù)據(jù)抽象是一個(gè)把實(shí)現(xiàn)細(xì)節(jié)與相關(guān)的數(shù)據(jù)分離開(kāi)的概念。

如果類(lèi)中至少有一個(gè)函數(shù)被聲明為純虛函數(shù),則這個(gè)類(lèi)就是抽象類(lèi)。純虛函數(shù)是通過(guò)在聲明中使用 "= 0" 來(lái)指定的,如下所示:

class Box
{
   public:
      // 純虛函數(shù)
      virtual double getVolume() = 0;
   private:
      double length;      // 長(zhǎng)度
      double breadth;     // 寬度
      double height;      // 高度
};

設(shè)計(jì)抽象類(lèi)(通常稱(chēng)為 ABC)的目的,是為了給其他類(lèi)提供一個(gè)可以繼承的適當(dāng)?shù)幕?lèi)。抽象類(lèi)不能被用于實(shí)例化對(duì)象,它只能作為接口使用。如果試圖實(shí)例化一個(gè)抽象類(lèi)的對(duì)象,會(huì)導(dǎo)致編譯錯(cuò)誤。

因此,如果一個(gè) ABC 的子類(lèi)需要被實(shí)例化,則必須實(shí)現(xiàn)每個(gè)虛函數(shù),這也意味著 C++ 支持使用 ABC 聲明接口。如果沒(méi)有在派生類(lèi)中重載純虛函數(shù),就嘗試實(shí)例化該類(lèi)的對(duì)象,會(huì)導(dǎo)致編譯錯(cuò)誤。

可用于實(shí)例化對(duì)象的類(lèi)被稱(chēng)為具體類(lèi)。

抽象類(lèi)的實(shí)例

請(qǐng)看下面的實(shí)例,基類(lèi) Shape 提供了一個(gè)接口 getArea(),在兩個(gè)派生類(lèi) Rectangle 和 Triangle 中分別實(shí)現(xiàn)了 getArea():

#include <iostream>
 
using namespace std;
 
// 基類(lèi)
class Shape 
{
public:
   // 提供接口框架的純虛函數(shù)
   virtual int getArea() = 0;
   void setWidth(int w)
   {
      width = w;
   }
   void setHeight(int h)
   {
      height = h;
   }
protected:
   int width;
   int height;
};
 
// 派生類(lèi)
class Rectangle: public Shape
{
public:
   int getArea()
   { 
      return (width * height); 
   }
};
class Triangle: public Shape
{
public:
   int getArea()
   { 
      return (width * height)/2; 
   }
};
 
int main(void)
{
   Rectangle Rect;
   Triangle  Tri;
 
   Rect.setWidth(5);
   Rect.setHeight(7);
   // 輸出對(duì)象的面積
   cout << "Total Rectangle area: " << Rect.getArea() << endl;
 
   Tri.setWidth(5);
   Tri.setHeight(7);
   // 輸出對(duì)象的面積
   cout << "Total Triangle area: " << Tri.getArea() << endl; 
 
   return 0;
}

當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:

Total Rectangle area: 35
Total Triangle area: 17

從上面的實(shí)例中,我們可以看到一個(gè)抽象類(lèi)是如何定義一個(gè)接口 getArea(),兩個(gè)派生類(lèi)是如何通過(guò)不同的計(jì)算面積的算法來(lái)實(shí)現(xiàn)這個(gè)相同的函數(shù)。

設(shè)計(jì)策略

面向?qū)ο蟮南到y(tǒng)可能會(huì)使用一個(gè)抽象基類(lèi)為所有的外部應(yīng)用程序提供一個(gè)適當(dāng)?shù)摹⑼ㄓ玫?、?biāo)準(zhǔn)化的接口。然后,派生類(lèi)通過(guò)繼承抽象基類(lèi),就把所有類(lèi)似的操作都繼承下來(lái)。

外部應(yīng)用程序提供的功能(即公有函數(shù))在抽象基類(lèi)中是以純虛函數(shù)的形式存在的。這些純虛函數(shù)在相應(yīng)的派生類(lèi)中被實(shí)現(xiàn)。

這個(gè)架構(gòu)也使得新的應(yīng)用程序可以很容易地被添加到系統(tǒng)中,即使是在系統(tǒng)被定義之后依然可以如此。

文章內(nèi)容摘自 菜鳥(niǎo)教程http://www.runoob.com/cplusplus/cpp-tutorial.html

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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