第二篇:C++ 構(gòu)造函數(shù)的奇葩問題

前文C++ 面向?qū)ο?類接口和類的實(shí)現(xiàn)

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

承接上文,這里先說帶參數(shù)的構(gòu)造函數(shù),自定義的構(gòu)造函數(shù)必須申明在類接口當(dāng)中,例如person.hh文件當(dāng)中虐沥。

class Person{
    .....
      public:
            Person();
            Person(
                std::string const &pname,
                std::string const &paddr,
                size_t const &age);
    .....
}

如果我們?nèi)匀幌M軌驑?gòu)造一個(gè)普通的Person對象而沒有任何特定的數(shù)據(jù)成員初始值,那么也必須顯式地聲明默認(rèn)構(gòu)造函數(shù)私股。 因此上面的Person類將支持兩個(gè)構(gòu)造函數(shù).

默認(rèn)構(gòu)造函數(shù)不必初始化Person對象的string據(jù)成員茂卦。 由于這些數(shù)據(jù)成員本身就是對象豁生,因此它們被自己的默認(rèn)構(gòu)造函數(shù)初始化為空字符串酸纲。 但是捣鲸,還有一個(gè)size_t數(shù)據(jù)成員。 該成員是內(nèi)置類型的變量闽坡,并且此類變量沒有構(gòu)造函數(shù)栽惶,因此不會(huì)自動(dòng)初始化愁溜。 因此,除非顯式初始化d_age數(shù)據(jù)成員的值外厂,否則其值為:

  • 對于局部的Person對象內(nèi)部會(huì)隨機(jī)分配一個(gè)整數(shù)值
  • 對于全局的Person對象會(huì)分配0冕象。

對于局部的Person對象來說,age的隨機(jī)值,可能不是一個(gè)好注意,所以我們可以在實(shí)現(xiàn)默認(rèn)的構(gòu)造函數(shù)來面初始化數(shù)據(jù)成員d_age

Person::Person(){
    d_age=0;
}

接下來說明使用帶有參數(shù)和不帶參數(shù)的構(gòu)造函數(shù)汁蝶。 對象yang由定義非空參數(shù)列表的構(gòu)造函數(shù)初始化渐扮,addy對象即會(huì)調(diào)用默認(rèn)的構(gòu)造函數(shù)初始化。

使用需要參數(shù)的構(gòu)造函數(shù)構(gòu)造對象時(shí)掖棉,建議用花括號(hào)括起參數(shù)席爽。 通常也可以使用括號(hào),有時(shí)甚至必須使用括號(hào)啊片。這是顯示兩個(gè)構(gòu)造函數(shù)調(diào)用的示例:

int main(int argc, char const *argv[])
{
    Person yang{"Yang Ming", "東風(fēng)路42號(hào)E棟二層 812號(hào)", 35};
    Person addy; //初始化默認(rèn)的構(gòu)造函數(shù)
    return 0;
}

如果使用默認(rèn)的參數(shù)值定義Person對象,則必須將相應(yīng)的構(gòu)造函數(shù)添加到Person的接口玖像。除了重載類構(gòu)造函數(shù)之外紫谷,還可以為構(gòu)造函數(shù)提供默認(rèn)參數(shù)值。 必須使用類接口中的構(gòu)造函數(shù)聲明指定這些默認(rèn)參數(shù)捐寥,如下所示:

class Person{
    public:
            Person(
                 std::string const &pname,
                 std::string const &paddr = "--未知--",
                 std::string const &phone = "--未知--",
                 size_t age = 0
          );
}

通常笤昨,構(gòu)造函數(shù)使用高度相似的實(shí)現(xiàn)。 這是因?yàn)闃?gòu)造函數(shù)的參數(shù)通常是為了方便而定義的:
Person的構(gòu)造函數(shù)不需要phone,但需要age,這樣的構(gòu)造函數(shù)不能使用默認(rèn)構(gòu)造函數(shù)定義握恳,因?yàn)閜hone不是構(gòu)造函數(shù)的最后一個(gè)參數(shù)瞒窒,因此,特殊的構(gòu)造函數(shù)不需要在其參數(shù)列表中包含phone.

類構(gòu)造函數(shù)的奇葩問題

示例1:
在C++中,定義一個(gè)類的對象變量,如果你之前有其他類似Java,C#的開發(fā)語言經(jīng)驗(yàn)話,這類語言通常會(huì)使用括號(hào)"( )"調(diào)用構(gòu)造函數(shù),但在C++可能會(huì)導(dǎo)致意外的后果乡洼。如下示例會(huì)說明這一問題, 假設(shè)Foo類是以下類接口:

#include <iostream>

class Foo
{
public:
    char a = '\0';
    char b = '\0';
    Foo();
    Foo(char);
    Foo(char, char);

    void display();
};

Foo類的實(shí)現(xiàn)

Foo::Foo(){};
Foo::Foo(char x){
    a = x;
}

Foo::Foo(char x, char y){
    a = x;
    b = y;
}

void Foo::display()
{
    std::cout << "----打印Foo內(nèi)部數(shù)據(jù)成員-----" << std::endl;
    std::cout << "Foo::a=" << a << std::endl;
    std::cout << "Foo::b=" << b << std::endl;
}

我們在調(diào)用的main函數(shù)中定義兩個(gè)Foo實(shí)例崇裁,分別使用第一個(gè)和第二個(gè)構(gòu)造函數(shù),同時(shí)在對象定義中使用括號(hào)束昵。 代碼看起來像這樣:

int main(int argc, char const *argv[])
{
    Foo c1();
    Foo c2('A');

    c1.display();
    c2.display();
    return 0;
}

C++ 11之后的編譯器在編譯前已經(jīng)報(bào)錯(cuò)了,更不用說編譯了:

error: request for member ‘display’ in ‘c1’, which is of non-class type ‘Foo()’
c1.display();

這里發(fā)生了什么拔稳? 首先,請注意編譯器引用的數(shù)據(jù)類型:Foo()是一個(gè)函數(shù)而不是Foo類型,那()是做什么的呢?

在回答這個(gè)問題之前锹雏,不妨拓寬一下我們的思路巴比。 假設(shè)在程序庫中有一個(gè)工廠函數(shù)fooFactory。 工廠函數(shù)創(chuàng)建并返回特定類型的對象礁遵。

Foo fooFactory(){
      return Foo();
}

此fooFactory函數(shù)返回一個(gè)Foo對象使用Foo類現(xiàn)實(shí)中的默認(rèn)構(gòu)造函數(shù)構(gòu)造轻绞。 因此,fooFactory不需要參數(shù)佣耐。 我們想在程序中使用fooFactory函數(shù)政勃,但必須聲明該函數(shù),所以我們將聲明添加到main,因?yàn)檫@是fooFactory將被使用的唯一位置兼砖。 它是一個(gè)函數(shù)稼病,不需要參數(shù)选侨,返回一個(gè)Foo對象:

//函數(shù)聲明
Foo fooFactory();

int main(){
      Foo obj=fooFactory();
}

這與我們上文示例1中main函數(shù)中定義的c1非常相似

Foo c1();

問題剖析:Foo c1()顯然不是c1對象的定義,而是c1()的函數(shù)聲明然走,返回一個(gè)Foo對象援制。 那么,這里發(fā)生了什么芍瑞,我們應(yīng)該如何使用Foo的默認(rèn)構(gòu)造函數(shù)定義Foo對象晨仑?

事實(shí)上,類似Foo c1()這樣的語句,你可以說它是一個(gè)函數(shù)的原型聲明也行,也可以是使用定義了Foo類型的一個(gè)對象拆檬,這個(gè)對象通過其Foo的默認(rèn)構(gòu)造函數(shù)構(gòu)建洪己。這是C ++語法的模糊性,根據(jù)語言的標(biāo)準(zhǔn)竟贯,C++編譯器總是優(yōu)先認(rèn)為Foo c1()類似的語句一個(gè)函數(shù)聲明其次再是一個(gè)對象定義答捕。

使用類的默認(rèn)構(gòu)造函數(shù)定義對象時(shí),有幾種方法可以避免這種模糊性的。

  • 比如上文示例中提過的和其他基本數(shù)據(jù)類型,例如Foo c1;
  • 使用花括號(hào){}初始化對象,例如:Foo c1{};
  • 使用賦值運(yùn)算符和匿名默認(rèn)構(gòu)造的Foo對象:Foo c1 = Foo {},或者Foo c1 = Foo()屑那。

上述上下文中的Foo()定義了默認(rèn)構(gòu)造的匿名Foo對象拱镐。 對于Foo c1 = Foo()這樣的語句。 新版本的C++編譯器持际,能夠分辨是Foo類型沃琅,而不是Foo()的函數(shù)聲明

對于帶參數(shù)的構(gòu)造函數(shù),以下定義一個(gè)Foo的對象的語句是等價(jià)的蜘欲。

Foo c1('A');
Foo c1{'A'};
Foo c1 = Foo('A');
Foo c1 = Foo{'A'};

但為了好辨認(rèn),建議使用花括號(hào)定義類的對象

類型 Foo 和 Foo()的區(qū)別

首先讓我們看一下Foo中的第二個(gè)構(gòu)造函數(shù)益眉。 它期望一個(gè)char類型。 當(dāng)使用第二個(gè)構(gòu)造函數(shù)定義另一個(gè)Foo對象姥份,但想使用char()將默認(rèn)的char類型的值傳遞給構(gòu)造函數(shù)郭脂。 我們知道這定義了一個(gè)默認(rèn)的char值,因?yàn)閏out << char()<<endl很好地顯示了''澈歉,并且char x = char() 也將x初始化為空字符朱庆。因此我們定義下面的一個(gè)很怪僻的帶char()作為參數(shù)的Foo構(gòu)造函數(shù)

Foo c1(char()) ;

error: request for member ‘display’ in ‘c1’, which is of non-class type ‘Foo(char (*)())’

糟糕,并沒有我們預(yù)期的那樣闷祥? 這怎么又跟指針突然指針扯上關(guān)系了呢娱颊?

像int(),char(),double(),或用戶定義的class/struct類型這類基本數(shù)據(jù)類型的初始化表達(dá)式,我們統(tǒng)一用一個(gè)符號(hào)表示為

Type()

它不僅表示Type類型的默認(rèn)值,而且它也是指向?qū)?yīng)函數(shù)的匿名指針的簡寫符號(hào)

而對于類似Foo這樣的類接口(或者叫類聲明)
實(shí)際上就是要告知C++編譯器,需要分配多少內(nèi)存空間容納Foo的數(shù)據(jù)成員以及成員函數(shù)

請牢記對于類似Type()的表達(dá)式,C++的編譯器它會(huì)優(yōu)先解析為一個(gè)函數(shù)原型的聲明,而函數(shù)聲明實(shí)質(zhì)就是告知編譯器對應(yīng)的函數(shù)指針,那么當(dāng)你使用Foo c1(char())的時(shí)候,C++編譯器實(shí)際上會(huì)理解為就是調(diào)用Foo c1(char (*)())凯砍,而這與我們Foo類接口中構(gòu)造函數(shù)Foo::Foo(char)是不符的箱硕。

下文:C++ 構(gòu)造函數(shù)的初始化

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市悟衩,隨后出現(xiàn)的幾起案子剧罩,更是在濱河造成了極大的恐慌,老刑警劉巖座泳,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惠昔,死亡現(xiàn)場離奇詭異幕与,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)镇防,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門啦鸣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人来氧,你說我怎么就攤上這事诫给。” “怎么了啦扬?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵中狂,是天一觀的道長。 經(jīng)常有香客問我扑毡,道長胃榕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任瞄摊,我火速辦了婚禮勋又,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泉褐。我一直安慰自己,他們只是感情好鸟蜡,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布膜赃。 她就那樣靜靜地躺著,像睡著了一般揉忘。 火紅的嫁衣襯著肌膚如雪跳座。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天泣矛,我揣著相機(jī)與錄音疲眷,去河邊找鬼。 笑死您朽,一個(gè)胖子當(dāng)著我的面吹牛狂丝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哗总,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼几颜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了讯屈?” 一聲冷哼從身側(cè)響起蛋哭,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涮母,沒想到半個(gè)月后谆趾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躁愿,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年沪蓬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了彤钟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡怜跑,死狀恐怖样勃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情性芬,我是刑警寧澤峡眶,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站植锉,受9級(jí)特大地震影響辫樱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜俊庇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一狮暑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辉饱,春花似錦搬男、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至姓惑,卻和暖如春褐奴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背于毙。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工敦冬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人唯沮。 一個(gè)月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓脖旱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親介蛉。 傳聞我的和親對象是個(gè)殘疾皇子夯缺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容