前言:
在C++98中,支持了在類聲明中使用等號“=”加初始值的方式,來初始化類中靜態成員常量。這種聲明方式我們也稱之為“就地”聲明。就地聲明在代碼編寫時非常便利,不過C++98對類中就地聲明的要求卻非常高。如果靜態成員不滿足常量性,則不可以就地聲明,而且即使常量的靜態成員也只能是整型或者枚舉型才能就地初始化。而非靜態成員變量的初始化則必須在構造函數中進行。
首先,先得了解一下C++支持哪幾種類成員初始化的方式,你常用的又是哪一種。
一、初始化方式
1、初始化方式一:初始化列表
1
2
3
4
5
6
|
class A { public : int a; // 初始化列表 A( int a_):a(a_){} }; |
2、初始化方式二:構造函數初始化
1
2
3
4
5
6
|
class A { public : int a; // 初始化列表 A( int a_, bool b) { a = a_; } }; |
3、初始化方式三:聲明時初始化(也稱就地初始化,c++11后支持)
1
2
3
4
5
6
|
class A { public : int a = 1; // 聲明時初始化 A() {} }; |
在C++98中,支持了在類聲明中使用等號“=”加初始值的方式,來初始化類中靜態成員常量。這種聲明方式我們也稱之為“就地”聲明。就地聲明在代碼編寫時非常便利,不過C++98對類中就地聲明的要求卻非常高。如果靜態成員不滿足常量性,則不可以就地聲明,而且即使常量的靜態成員也只能是整型或者枚舉型才能就地初始化。而非靜態成員變量的初始化則必須在構造函數中進行。比如,如下代碼在c++98中編譯
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Init { public : Init(): a(0) [] Init( int d): a(d) {} private : int a; const static int b = 0; int c = 1; // member, cannot pass build static int d = 0; // member, cannot pass build static const double e = 1.3; // not int or enum type, cannot pass build stati const char * const f = "e" ; // not int or enum type, cannot pass build } |
這非常不方便,所以在C++11中,標準允許非靜態成員變量的初始化有多種形式。具體而言,除了初始化列表外,在C++11中,標準還允許使用等號= 或者 花括號{} 進行就地的非靜態成員變量初始化。
1
2
3
4
|
struct init { int a = 1; double b {1.2}; }; |
大家知道,有幾種情況下推薦優先使用列表初始化
- const成員變量只能用成員初始化列表來完成初始化,而不能在構造函數內賦值
- 初始化的數據成員是對象
- 需要初始化引用成員數據
具體的原因這里不細述,大家可以去看一下《C++ Primer》。
構造函數初始化的本質是賦值操作("="),這個方法存在兩個問題,一個是比起初始化列表和就地初始化,此方式的效率偏低;第二個是可能存在錯誤隱患。
先說第一個,賦值過程中會產生臨時對象,臨時對象的構造析構會造成效率損耗,初始化列表的方式就避免了產生臨時對象縮帶來的問題。
第二個是,如果你沒有重寫或者禁止賦值構造函數,c++會悄悄的加上默認的賦值構造函數,這個時候也有可能帶來問題。
從C++11之后,這三種初始化的方法都可以使用,并不會存在沖突,但是,他們之間是有優先級順序的,這個優先級來源于他們在初始化的時間順序,后面初始化的會把前面的覆蓋掉,成員變量的初始化順序是
二、聲明時初始化->初始化列表->構造函數初始化
因此假如三種初始化方式同時存在的話,那么最后保留的成員變量值肯定是構造函數中初始化的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#include <iostream> using namespace std; class A { public : int a = 1; A( int a_) :a(2) { a = 3; } }; int main() { A a; cout << "a.a=" << a.a << endl; return 0; } // a.a=3 |
既然初始化方式這么多,那么什么時候適用哪種呢?
1、聲明時初始化的使用場景
一個優點是直觀,你在聲明的時候順便給一個初始值,bravo
,別人在看你代碼的時候,點一下調到聲明也能看到你賦予的初始值,不用再去看構造函數那里給的什么值
第二個優點更有用了,比如你要定義多個構造函數,每個構造函數都用列表初始化的方法初始化,多麻煩呀,請看下面的例子,媽媽看了再也不用擔心我想用其他初始化方法了
1
2
3
4
5
6
7
8
9
10
11
|
class Group { public : Group() {} Group( int a): data(a) {} Group(Mem m): mem(m) {} Group( int a, Mem m, string n): data(a), mem(m), name(n) {} private : int data = 1; Mem mem{0}; string name{ "Group" }; }; |
2、列表初始化的使用場景
前面說過了三個場景,這里贅述一下
-
const
成員變量只能用成員初始化列表來完成初始化,而不能在構造函數內賦值 - 初始化的數據成員是對象
- 需要初始化引用成員數據
但是,需要注意列表初始化的順序,不過IDE會提示你的
3、構造函數初始化的使用場景
第一個就是拷貝和賦值構造函數里(不然怎么叫賦值構造函數呢)
第二個就是比較無聊的情況了,比如你想把幾個成員函數都初始化成一個值,請看下面例子
1
2
3
4
5
6
7
8
|
class Group { public : Group() {data1 = data2 = data3 = 0;} private : int data1; int data2; int data3; }; |
一言以蔽之,優先就地初始化和列表初始化。
到此這篇關于C++類成員初始化的三種方式的文章就介紹到這了,更多相關C++類成員初始化的方式內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://bbs.huaweicloud.com/blogs/281096