第十四章 高級函數 14.1重載成員函數 函數可以進行重載,成員函數(成員方法)實質上也是一種函數,所以成員函數也可以進行重載。 程式清單14.1 Rectangle.cpp #include <iostream> class Rectangle { private: int width; in ...
第十四章 高級函數
14.1重載成員函數
函數可以進行重載,成員函數(成員方法)實質上也是一種函數,所以成員函數也可以進行重載。
程式清單14.1 Rectangle.cpp
#include <iostream>
class Rectangle
{
private:
int width;
int height;
public:
Rectangle(int width, int height);
~Rectangle(){};
void drawShape() const;
void drawShape(int width, int height) const;
};
Rectangle::Rectangle(int width, int height)
{
this->height = height;
this->width = width;
}
void Rectangle::drawShape() const
{
drawShape(width, height);
}
void Rectangle::drawShape(int width, int height) const
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
std::cout << "*";
}
std::cout << std::endl;
}
}
int main()
{
Rectangle box(30, 5);
std::cout << "drawShape():" << std::endl;
box.drawShape();
std::cout << "\ndrawShape(40,2):" << std::endl;
box.drawShape(40, 2);
return 0;
}
編譯器根據參數的類型和數值決定調用哪個版本。
14.2使用預設值
常規函數可以有一個或多個預設值,類的成員函數也是如此。聲明預設值的規則也相同。
程式清單14.2 Rectangle2.cpp
#include <iostream>
class Rectangle
{
private:
int width;
int height;
public:
Rectangle(int weight, int height);
~Rectangle() {}
void drawShape(int aWidth, int aHeight, bool useCurrentValue = false) const;
};
Rectangle::Rectangle(int width, int height)
{
this->width = width;
this->height = height;
}
void Rectangle::drawShape(int aWidth, int aHeight, bool useCurrentValue) const
{
int printWidth = 0;
int printHeight = 0;
if (useCurrentValue == true)
{
printWidth = width;
printHeight = height;
}
else
{
printWidth = aWidth;
printHeight = aHeight;
}
for (int i = 0; i < printHeight; i++)
{
for (int j = 0; j < printWidth; j++)
{
std::cout << "*";
}
std::cout << std::endl;
}
}
int main()
{
Rectangle box(20, 5);
std::cout << "drawShape(0,0,true) ..." << std::endl;
box.drawShape(0, 0, true);
std::cout << "drawShape(25,4) ..." << std::endl;
box.drawShape(25, 4);
return 0;
}
14.3初始化對象
與成員函數一樣,構造函數也可以重載。
可以重載構造函數,但不能重載析構函數。析構函數的簽名總是這樣的:名稱為類名前加~,且不接受任何參數。
構造函數由兩部分組成:初始化部分和函數體。可在初始化部分設置成員變數(即初始化列表),也可在構造函數的函數體內設置。初始化列表舉例:
Tricycle::Tricycle():speed(5),wheelSize(12) { //函數體 }
初始化成員變數的效率比在函數體內給他們賦值高。
14.4複製構造函數
又稱拷貝構造函數
除提供預設構造函數和析構函數之外,編譯器還提供一個預設複製構造函數。每當創建對象的備份時,都將調用複製構造函數。
按值將對象傳入或傳出函數時,都將創建對象的一個臨時備份。如果對象是用戶定義的,就將調用相應類的複製構造函數。
所有複製構造函數都接受一個參數:一個引用,它指向所屬類的對象。最好將該引用聲明為常量,因為複製構造函數不用修改傳入的對象,例如:
Tricycle(const Tricycle &trike);
預設構造函數只作為參數傳入的對象的每個成員變數複製到新對象中,這稱為淺複製(淺拷貝)。雖然對大多數成員變數來說沒問題,但是不適用於成員變數是指向堆中對象的指針這種情況。
淺複製將一個對象的成員變數的值複製到另一個對象中,這導致兩個對象中的指針指向相同的記憶體地址。另一方面,深複製將堆記憶體中的值複製到新分配的堆記憶體中。
淺複製使得兩個或多個變數指向相同記憶體,如果當中一個不再在作用域內,就會導致導致調用析構函數釋放分配的記憶體,而剩下的變數仍指向該記憶體,試圖訪問該記憶體將導致程式崩潰。
對於這種問題,解決方案是自定義複製構造函數,併在複製時正確分配記憶體。
程式清單14.3 DeepCopy.cpp
#include <iostream>
class Tricycle
{
private:
int *speed;
public:
Tricycle();
Tricycle(const Tricycle &);
~Tricycle();
int getSpeed() const { return *speed; }
void setSpeed(int newSpeed) { *speed = newSpeed; }
void pedal();
void brake();
};
Tricycle::Tricycle()
{
speed = new int;
*speed = 5;
}
Tricycle::Tricycle(const Tricycle &rhs)
{
speed = new int;
*speed = rhs.getSpeed();
}
Tricycle::~Tricycle()
{
delete speed;
speed = NULL;
}
void Tricycle::pedal()
{
setSpeed(*speed + 1);
std::cout << "\nPedaling " << getSpeed() << " mph" << std::endl;
}
void Tricycle::brake()
{
setSpeed(*speed - 1);
std::cout << "\nPedaling " << getSpeed() << " mph" << std::endl;
}
int main()
{
std::cout << "Creating trike named wichita ...";
Tricycle wichita;
wichita.pedal();
std::cout << "Creating trike named dallas ..." << std::endl;
Tricycle dallas(wichita);
std::cout << "wichita's speed: " << wichita.getSpeed() << std::endl;
std::cout << "dallas's speed: " << dallas.getSpeed() << std::endl;
std::cout << "setting wichita to 10 ..." << std::endl;
wichita.setSpeed(10);
std::cout << "wichita's speed: " << wichita.getSpeed() << std::endl;
std::cout << "dallas's speed: " << dallas.getSpeed() << std::endl;
return 0;
}
14.5編譯階段常量表達式
c++編譯器竭盡所能地提高程式的運行速度——儘可能對編寫的代碼進行優化。一種存在效率提高空間的簡單情況是將兩個常量相加,如下所示:
const int decade = 10; int year = 2016 + decade;
2016與decade都是常量,所以編譯器將計算這個表達式並將結果2026存儲。因此,在編譯器看來,就像是將2026賦給了year一樣。
函數可使用const來返回常量值,如下所示:
const int getCentury() { return 100; }
你可能會認為如果調用語句
int year = 2016 + getCentury();
編譯器會對錶達式存在優化空間;雖然這個成員函數返回的是一個常量,但這個函數本身不是const的,它可能修改全局變數或調用非const成員函數。常量表達式是c++新增的功能,用關鍵字constexpr表示:
constexpr int getCentury() { return 100; }
常量表達式必須返回一個表達式,而該表達式只能包含字面值、其他常量表達式或使用constexpr定義的變數。
程式清單14.4 Circle.cpp
#include <iostream>
constexpr double getPi()
{
return (double)22 / 7; //獲取Π的近似值
}
int main()
{
float radius;
std::cout << "Enter the radius of the circle: ";
std::cin >> radius;
double area = getPi() * (radius * radius);
std::cout << "\nCircle's area: " << area << std::endl;
return 0;
}