C++聚合體初始化aggregate initialization詳細介紹
聚合體初始化(aggregate initialization)
C++有很多初始化對象的方法。其中之一叫做 聚合體初始化(aggregate initialization) ,這是聚合體專有的一種初始化方法。
從C語言引入的初始化方式是用花括號括起來的一組值來初始化類:
struct Data {
std::string name;
double value;
};
Data x = {"test1", 6.778};
自從C++11起,你可以忽略等號:
Data x{<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->"test1", 6.778};自從C++17起,聚合體可以擁有基類。也就是說像下面這種從其他類派生出的子類也可以使用這種初始化方法:
struct MoreData : Data {
bool done;
}
MoreData y{{"test1", 6.778}, false};
如你所見,聚合體初始化時可以用一個子聚合體初始化來初始化類中來自基類的成員。
另外,你甚至可以省略子聚合體初始化的花括號:
MoreData y{<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->"test1", 6.778, false};這樣寫將遵循嵌套聚合體初始化時的通用規(guī)則,你傳遞的實參被用來初始化哪一個成員取決于它們的順序。
擴展聚合體初始化的動機
如果沒有這個特性,那么所有的派生類都不能使用聚合體初始化,這意味著你要像下面這樣定義構(gòu)造函數(shù):
struct Cpp14Data : Data {
bool done;
Cpp14Data (const std::string& s, double d, bool b) : Data{s, d}, done {
}
};
Cpp14Data y{"test1", 6.778, false};
現(xiàn)在我們不再需要定義任何構(gòu)造函數(shù)就可以做到這一點。
我們可以直接使用嵌套花括號的語法來實現(xiàn)初始化,
如果給出了內(nèi)層初始化需要的所有值就可以省略內(nèi)層的花括號:
MoreData x{{"test1", 6.778}, false}; // 自從C++17起OK
MoreData y{"test1", 6.778, false}; // OK
注意因為現(xiàn)在派生類也可以是聚合體,所以其他的一些初始化方法也可以使用:
MoreData u; // OOPS:value/done未初始化
MoreData z{}; // OK: value/done初始化為0/false
如果覺得這樣很危險,可以使用成員初始值:
struct Data {
std::string name;
double value{0.0};
};
struct Cpp14Data : Data {
bool done{false};
};
或者,繼續(xù)提供一個默認構(gòu)造函數(shù)。
使用聚合體擴展
聚合體初始化的一個典型應(yīng)用場景是對一個派生自C風(fēng)格結(jié)構(gòu)體并且添加了新成員的類進行初始化。例如:
struct Data {
const char* name;
double value;
};
struct CppData : Data {
bool critical;
void print() const {
std::cout << '[' << name << ',' << value << "]\n";
}
};
CppData y{{"test1", 6.778}, false};
y.print();
這里,內(nèi)層花括號里的參數(shù)被傳遞給基類Data。
注意你可以跳過初始化某些值。在這種情況下,跳過的成員將會進行默認初始化
(基礎(chǔ)類型會被初始化為0、false或者nullptr,類類型會默認構(gòu)造)。
例如:
CppData x1{}; // 所有成員默認初始化為0值
CppData x2{{"msg"}} // 和{{"msg", 0.0}, false}等價
CppData x3{{}, true}; // 和{{nullptr, 0.0}, true}等價
CppData x4; // 成員的值未定義
注意使用空花括號和不使用花括號完全不同:
x1的定義會把所有成員默認初始化為0值,
因此字符指針name被初始化為nullptr,
double類型的value初始化為0.0,
bool類型的flag初始化為false。x4的定義沒有初始化任何成員。所有成員的值都是未定義的。
你也可以從非聚合體派生出聚合體。例如:
struct MyString : std::string {
void print() const {
if (empty()) {
std::cout << "<undefined>\n";
}
else {
std::cout << c_str() << '\n';
}
}
};
MyString x{{"hello"}};
MyString y{"world"};
注意這不是通常的具有多態(tài)性的public繼承,因為std::string沒有虛成員函數(shù),
你需要避免混淆這兩種類型。
你甚至可以從多個基類和聚合體中派生出聚合體:
template<typename T>
struct D : std::string, std::complex<T>
{
std::string data;
};
你可以像下面這樣使用和初始化:
D<float> s{{"hello"}, {4.5, 6.7}, "world"}; // 自從C++17起OK
D<float> t{"hello", {4.5, 6.7}, "world"}; // 自從C++17起OK
std::cout << s.data; // 輸出:"world"
std::cout << static_cast<std::string>(s); // 輸出:"hello"
std::cout << static_cast<std::complex<float>>(s); //輸出:(4.5,6.7)
內(nèi)部嵌套的初值列表將按照繼承時基類聲明的順序傳遞給基類。
這個新的特性也可以幫助我們用很少的代碼定義重載的lambda。
聚合體的定義
總的來說,在C++17中滿足如下條件之一的對象被認為是 聚合體 :
- 是一個數(shù)組
- 或者是一個滿足如下條件的 類類型 (
class、struct、union): - 沒有用戶定義的和
explicit的構(gòu)造函數(shù) - 沒有使用
using聲明繼承的構(gòu)造函數(shù) - 沒有
private和protected的非靜態(tài)數(shù)據(jù)成員 - 沒有
virtual函數(shù) - 沒有
virtual, private, protected的基類
然而,要想使用聚合體初始化來 初始化 聚合體,那么還需要滿足如下額外的約束:
- 基類中沒有
private或者protected的成員 - 沒有
private或者protected的構(gòu)造函數(shù)
下一節(jié)就有一個因為不滿足這些額外約束導(dǎo)致編譯失敗的例子。
C++17引入了一個新的類型特征is_aggregate<>
來測試一個類型是否是聚合體:
template<typename T>
struct D : std::string, std::complex<T> {
std::string data;
};
D<float> s{{"hello"}, {4.5, 6.7}, "world"}; // 自從C++17起OK
std::cout << std::is_aggregate<decltype(s)>::value; // 輸出1(true)
向后的不兼容性
注意下面的例子不能再通過編譯:
struct Derived;
struct Base {
friend struct Derived;
private:
Base() {
}
};
struct Derived : Base {
};
int main()
{
Derived d1{}; // 自從C++17起ERROR
Derived d2; // 仍然OK(但可能不會初始化)
}
在C++17之前,Derived不是聚合體。因此
Derived d1{};
會調(diào)用Derived隱式定義的默認構(gòu)造函數(shù),這個構(gòu)造函數(shù)會調(diào)用基類Base的構(gòu)造函數(shù)。
盡管基類的默認構(gòu)造函數(shù)是private的,但在派生類的構(gòu)造函數(shù)里調(diào)用它也是有效的,
因為派生類被聲明為友元類。
自從C++17起,例子中的Derived是一個聚合體,所以它沒有隱式的默認構(gòu)造函數(shù)
(構(gòu)造函數(shù)沒有使用using聲明繼承)。因此,d1的初始化將是一個聚合體初始化,
如下表達式:
std::is_aggregate<Derived>::value
將返回true。
然而,因為基類有一個private的構(gòu)造函數(shù)(見上一節(jié))所以不能使用花括號來初始化。
這和派生類是否是基類的友元無關(guān)。
到此這篇關(guān)于C++聚合體初始化aggregate initialization詳細介紹的文章就介紹到這了,更多相關(guān)C++聚合體初始化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實現(xiàn)LeetCode(106.由中序和后序遍歷建立二叉樹)
這篇文章主要介紹了C++實現(xiàn)LeetCode(106.由中序和后序遍歷建立二叉樹),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07
在Visual Studio中用C++語言創(chuàng)建DLL動態(tài)鏈接庫圖文教程
這篇文章主要介紹了在Visual Studio中用C++語言創(chuàng)建DLL動態(tài)鏈接庫圖文教程,本文詳細講解了DLL庫的創(chuàng)建過程,并給出了代碼示例,需要的朋友可以參考下2014-09-09
詳解C++設(shè)計模式編程中責任鏈模式的應(yīng)用
這篇文章主要介紹了C++設(shè)計模式編程中責任鏈模式的應(yīng)用,責任鏈模式使多個對象都有機會處理請求,從而避免請求的發(fā)送者和接收者之間的耦合關(guān)系,需要的朋友可以參考下2016-03-03

