C++基礎教程面向物件(學習筆記5(2))
非靜態成員初始化
在編寫具有多個建構函式的類(大多數建構函式)時,必須為每個建構函式中的所有成員指定預設值會導致冗餘程式碼。如果更新成員的預設值,則需要觸控每個建構函式。
從C ++ 11開始,可以直接為普通類成員變數(不使用static關鍵字的變數)提供預設初始化值:
class Rectangle { private: double m_length = 1.0; // m_length 的預設值是1.0 double m_width = 1.0; // m_width 的預設值是1.0 public: Rectangle() { //此建構函式將使用上面的預設值,因為它們不會在此處重寫 } void print() { std::cout << "length: " << m_length << ", width: " << m_width << '\n'; } }; int main() { Rectangle x; // x.m_length = 1.0, x.m_width = 1.0 x.print(); return 0; }
該程式產生結果: length:1.0,width: 1.0 非靜態成員初始化(也稱為類內成員初始值設定項)為建構函式將提供的成員變數提供預設值,如果建構函式不為成員本身提供初始化值(通過成員初始化列表)。
但是,注意建構函式仍然確定可以建立哪種物件。考慮以下情況:
class Rectangle { private: double m_length = 1.0; double m_width = 1.0; public: //注意: 這個例子不提供預設建構函式 Rectangle(double length, double width) : m_length(length), m_width(width) { // m_length 和m_width 通過建構函式被初始化 (預設值就不用啦!) } void print() { std::cout << "length: " << m_length << ", width: " << m_width << '\n'; } }; int main() { Rectangle x; // 即使成員具有預設初始化值,也不會編譯,因為不存在預設建構函式 return 0; }
即使我們為所有成員提供了預設值,也沒有提供預設建構函式,因此我們無法建立沒有引數的Rectangle物件。
如果提供了預設初始化值並且建構函式通過成員初始值設定項列表初始化成員,則成員初始值設定項列表將優先。以下示例顯示了這一點:
class Rectangle { private: double m_length = 1.0; double m_width = 1.0; public: Rectangle(double length, double width) : m_length(length), m_width(width) { // m_length 和m_width 通過建構函式被初始化 (預設值就不用啦!) } void print() { std::cout << "length: " << m_length << ", width: " << m_width << '\n'; } }; int main() { Rectangle x(2.0, 3.0); x.print(); return 0; }
這列印:
length:2.0,width:3.0 請注意,使用非靜態成員初始化初始化成員需要使用等號或大括號(統一)初始化程式 - 直接初始化表單在此處不起作用。
規則:支援使用非靜態成員初始化為您的成員變數提供預設值。
Quiz time
1)更新以下程式以使用非靜態成員初始化和成員初始化列表。 2)為什麼我們需要在上面的程式中宣告一個空的預設建構函式,因為所有成員都是通過非靜態成員初始化初始化的?
#include <string>
#include <iostream>
class Ball
{
private:
std::string m_color;
double m_radius;
public:
//沒有任何預設引數的建構函式
Ball()
{
m_color = "black";
m_radius = 10.0;
}
// 建構函式僅僅有color引數 (radius採用預設值)
Ball(const std::string &color)
{
m_color = color;
m_radius = 10.0;
}
// 建構函式僅僅有radius引數 (color採用預設值)
Ball(double radius)
{
m_color = "black";
m_radius = radius;
}
// 具有color和radius引數的建構函式
Ball(const std::string &color, double radius)
{
m_color = color;
m_radius = radius;
}
void print()
{
std::cout << "color: " << m_color << ", radius: " << m_radius << '\n';
}
};
int main()
{
Ball def;
def.print();
Ball blue("blue");
blue.print();
Ball twenty(20.0);
twenty.print();
Ball blueTwenty("blue", 20.0);
blueTwenty.print();
return 0;
}
該程式應該產生結果:
color:black,radius:10 color:blue,radius:10 color:black,radius:20 color:blue,radius:20
解決方案:
#include<iostream>
#include<string>
using namespace std;
class Ball
{
public:
//預設建構函式不提供任何引數,每一個引數都採用預設值
Ball()
{
}
//m_radius採用預設引數,這裡僅僅提供m_color引數
Ball(const string &color) :m_color(color)
{
}
//m_color採用預設引數,這裡僅僅提供m_radius引數
Ball(double radius) :m_radius(radius)
{
}
//這裡提供m_radius和m_color引數
Ball(const string &color, double radius) :m_color(color), m_radius(radius)
{
}
void print();
private:
string m_color="black";
double m_radius = 10;
};
void Ball::print()
{
cout << "顏色:" << m_color << ",半徑" << m_radius << endl;
}
int main()
{
Ball def;
def.print();
Ball blue("blue");
blue.print();
Ball twenty(20.0);
twenty.print();
Ball blueTwenty("blue", 20.0);
blueTwenty.print();
return 0;
}
問題2:為什麼我們需要在上面的程式中宣告一個空的預設建構函式,因為所有成員都是通過非靜態成員初始化初始化的? 回答:“Ball def;”將尋找一個預設建構函式來處理物件的例項化。如果該預設建構函式不存在(即使它是空的),編譯器將丟擲錯誤。