C++你可能不知道地方小結(jié)
更新時(shí)間:2013年01月02日 16:40:00 作者:
c++中編譯器替我們完成了許多事情,我們可能不知道,但也可能習(xí)以為常
下面詳細(xì)介紹
一、初始化與初始賦值
首先說(shuō)說(shuō)類(lèi)的初始化與初始賦值之前的區(qū)別,這也許里面可能有我們不知道的事情。
其實(shí)類(lèi)初始化與初始賦值還是有區(qū)別的。
class People{
public:
People(std::string name,int age,int height);
private:
std::string m_sName;
int m_iAge;
int m_iHeight;
}
//賦值
People::People(std::string name,int age,int height)
{
m_sName=name;
m_iAge=age;
m_iHeight=height;
}
//初始化列表
People::People(std::string name,int age,int height)
:m_sName(name),m_iAge(age),m_iHeight(height)
{}
C++規(guī)定,對(duì)象的成員變量初始化動(dòng)作發(fā)生在進(jìn)入構(gòu)造函數(shù)本體之前。在構(gòu)造函數(shù)內(nèi)成員變量賦值都不是初始化,而是賦值。
賦值時(shí)首先調(diào)用默認(rèn)構(gòu)造函數(shù)為m_sName,m_iAge,m_iHeight賦初始值,然后在立刻調(diào)用賦值操作符進(jìn)行賦新值。
成員初始列表是將各個(gè)成員變量實(shí)參都作為復(fù)制構(gòu)造函數(shù)的實(shí)參。
所以看出賦值相對(duì)于初始化,多了一步就是使用賦值操作符進(jìn)行賦值。所以初始化的效率比賦值的效率高多了。但是對(duì)于內(nèi)置類(lèi)型,它們效率是一樣的。
二、空類(lèi)
想想你如果聲明一個(gè)空類(lèi),C++編譯器會(huì)對(duì)它做什么呢?編譯器就會(huì)為它聲明一個(gè)復(fù)制構(gòu)造函數(shù),賦值操作符和一個(gè)析構(gòu)函數(shù),以及默認(rèn)構(gòu)造函數(shù)。所有這些函數(shù)都是public而且inline函數(shù)。
編譯器寫(xiě)的賦值構(gòu)造函數(shù)和賦值操作符,只是單純地將來(lái)源對(duì)象的每個(gè)non-static變量拷貝到目標(biāo)對(duì)象,具體是進(jìn)行位拷貝。
如果聲明了一個(gè)構(gòu)造函數(shù),編譯器是不會(huì)創(chuàng)建默認(rèn)構(gòu)造函數(shù)。
如果不希望類(lèi)支持拷貝構(gòu)造函數(shù)與賦值操作符怎么辦?不聲明?按照上面說(shuō)明編譯器會(huì)自動(dòng)幫你生成。那么可以將它們聲明為private,這樣阻止編譯器自動(dòng)生成拷貝構(gòu)造函數(shù)(public)。private成功阻止他人使用,但是這并不安全。因?yàn)轭?lèi)成員函數(shù)以及友元函數(shù)還是可以調(diào)用private的拷貝構(gòu)造函數(shù)和賦值操作符。
如果只在private下聲明拷貝函數(shù)和賦值操作符,在有人通過(guò)類(lèi)成員函數(shù)去以及member函數(shù)去調(diào)用它,會(huì)獲得一個(gè)連接錯(cuò)誤。那么這里能不能將錯(cuò)誤在編譯的時(shí)候體現(xiàn)出來(lái)呢?這里只用將拷貝函數(shù)聲明為private,并且不在自身,就可以辦到了。顯然繼承一個(gè)拷貝函數(shù)和賦值操作符為private的基類(lèi)就辦到了,基類(lèi)如下:
class NonCopyable{
protected:
NonCopyable (){}
~ NonCopyable (){}
private:
NonCopyable (const NonCopyable &);
NonCopyable & operater=(const NonCopyable &);
};
原因是類(lèi)成員函數(shù)或者友元函數(shù)嘗試拷貝對(duì)象,編譯器便會(huì)嘗試生成一個(gè)復(fù)制構(gòu)造函數(shù)與賦值操作符,并會(huì)調(diào)用基類(lèi)的對(duì)應(yīng)函數(shù),但是會(huì)被拒絕,因?yàn)榛?lèi)這些函數(shù)是private。
3、++函數(shù)
下面說(shuō)說(shuō)“*++"與"++*"中你不知道的事情,c++規(guī)定后綴形式自加函數(shù)有一個(gè)int類(lèi)型參數(shù),當(dāng)函數(shù)被調(diào)用時(shí),便其一傳遞一個(gè)0作為int參數(shù)的值傳遞給該函數(shù),而前綴形式自己函數(shù),類(lèi)型參數(shù)沒(méi)有要求,所以這樣就能區(qū)分一個(gè)++函數(shù)是前綴形式與后綴形式了,具體代碼如下:
class UPInt{
public
UPInt& operator++( ) ; //++ 前綴
const UPInt operator++( int ); //++后綴
UPInt& operator --( ); // --前綴
const UPInt operator --( int ) //--后綴
UPInt& operator +=( int ); //
...
};
UPInt & UPInt::operator++( )
{
*this += 1;
return *this;
}
const UPInt UPInt :: operator++( int )
{
UPInt oldValue = *this;
++(*this);
return oldValue;
}
后綴函數(shù)使用返回參數(shù)類(lèi)型const,是為了避免下面代碼生效
UPInt i;
i++++;
這個(gè)時(shí)候第一次調(diào)用++返回cosnt對(duì)象,并再次調(diào)用然后這個(gè)函數(shù)是non-const成員函數(shù),所以const對(duì)象無(wú)法調(diào)用這個(gè)函數(shù),那么i++++就無(wú)法生效了。
這里說(shuō)說(shuō)效率問(wèn)題,我們可以看到后綴++函數(shù)建立一個(gè)臨時(shí)對(duì)象以作為它返回值,這個(gè)臨時(shí)對(duì)象經(jīng)過(guò)構(gòu)造并在最后被析構(gòu)。而前綴++函數(shù)沒(méi)有這樣的臨時(shí)變量,并且沒(méi)有那樣的操作。所以如果我們?cè)诔绦蛑惺褂们熬Y++效率會(huì)更加高一些,沒(méi)有了臨時(shí)變量的構(gòu)造與析構(gòu)的動(dòng)作。
4.虛析構(gòu)函數(shù)
帶有多態(tài)性質(zhì)的base class應(yīng)該聲明一個(gè)virtual析構(gòu)函數(shù)。
為什么這么說(shuō)呢?看下面例子
class base
{ ... }
class derived:public base
{... }
base * p= new derived;
假設(shè)這里基類(lèi)的析構(gòu)函數(shù)不是virtual,當(dāng)使用完p指針,我們刪除它的時(shí)候,想想會(huì)發(fā)生什么,因?yàn)榛?lèi)的析構(gòu)函數(shù)是non-virtual所以不會(huì)發(fā)生多態(tài)直接調(diào)用基類(lèi)析構(gòu)函數(shù),僅僅刪除繼承類(lèi)中基類(lèi)那部分內(nèi)容,那么繼承類(lèi)對(duì)象其他內(nèi)存沒(méi)有被銷(xiāo)毀,從而資源泄漏。
如果將其聲明為virtual,那么就會(huì)發(fā)生多態(tài),調(diào)用的是指向繼承類(lèi)的指針,那么就會(huì)銷(xiāo)毀的是整個(gè)繼承類(lèi)象。
5.傳遞方式用引用
缺省情況下c++以值傳遞方式傳遞對(duì)象至函數(shù)。函數(shù)參數(shù)都是以實(shí)際實(shí)參的復(fù)件為初值,而調(diào)用端所獲得的是函數(shù)返回值的一個(gè)附件。這些復(fù)件都是由拷貝構(gòu)造函數(shù)產(chǎn)出??慈缦吕?
class Person{
public:
Person();
virtual ~Person();
...
private:
std::string name;
std::string address;
}
class Student:public Person{
public:
Student();
~Student();
...
private:
std::string schoolName;
std::string schoolAddress;
};
那么如果有一個(gè)函數(shù)驗(yàn)證是否為學(xué)生
bool validateStudent(Student s);
Student plato;
bool platoIsOK=validateStudent(plato);
分析這3行代碼,編譯器到底做了什么?首先調(diào)用Student的copy構(gòu)造函數(shù),然后以plato為藍(lán)本將s初始化,當(dāng)validateStudent返回被銷(xiāo)毀,所以成本為"一次Student copy構(gòu)造函數(shù)調(diào)用,加上一次Student析構(gòu)函數(shù)調(diào)用"。
Student對(duì)象內(nèi)部有兩個(gè)string對(duì)象,所以構(gòu)造了兩個(gè)string對(duì)象。Student繼承自Person對(duì)象,里面又有兩個(gè)string對(duì)象。所以by value方式傳遞一個(gè)Student對(duì)象,總體成本是"六次構(gòu)造函數(shù)和六次析構(gòu)函數(shù)"!
以by reference方式傳遞參數(shù)也可避免對(duì)象切割問(wèn)題。當(dāng)一個(gè)derived class對(duì)象以by value方式傳遞并被視為一個(gè)base class對(duì)象,base class的copy構(gòu)造函數(shù)會(huì)被調(diào)用,造成像derived class對(duì)象全被切割掉了,僅僅留下base class對(duì)象。看如下代碼通過(guò)傳遞引用參數(shù)完成多態(tài)
class Window{
public:
...
std::string name() const;
virtual void display() const;
};
class WindowWithScrollBars:public Window{
public:
...
virtual void display() const;
};
//傳入Windos類(lèi)型,調(diào)用其display函數(shù)
//傳入WindowWithScrollBars類(lèi)型,調(diào)用其display函數(shù)
//體現(xiàn)多態(tài)
void printNameAndDispaly(const Window& w)
{
std::cout<<w.name();
w.display();
}
窺視c++編譯器的底層,reference往往以指針實(shí)現(xiàn)出來(lái),因此pass by reference真正傳遞的是指針。如果對(duì)象屬于內(nèi)置型,pass by value往往比pass by reference 效率高些。
一、初始化與初始賦值
首先說(shuō)說(shuō)類(lèi)的初始化與初始賦值之前的區(qū)別,這也許里面可能有我們不知道的事情。
其實(shí)類(lèi)初始化與初始賦值還是有區(qū)別的。
復(fù)制代碼 代碼如下:
class People{
public:
People(std::string name,int age,int height);
private:
std::string m_sName;
int m_iAge;
int m_iHeight;
}
//賦值
People::People(std::string name,int age,int height)
{
m_sName=name;
m_iAge=age;
m_iHeight=height;
}
//初始化列表
People::People(std::string name,int age,int height)
:m_sName(name),m_iAge(age),m_iHeight(height)
{}
C++規(guī)定,對(duì)象的成員變量初始化動(dòng)作發(fā)生在進(jìn)入構(gòu)造函數(shù)本體之前。在構(gòu)造函數(shù)內(nèi)成員變量賦值都不是初始化,而是賦值。
賦值時(shí)首先調(diào)用默認(rèn)構(gòu)造函數(shù)為m_sName,m_iAge,m_iHeight賦初始值,然后在立刻調(diào)用賦值操作符進(jìn)行賦新值。
成員初始列表是將各個(gè)成員變量實(shí)參都作為復(fù)制構(gòu)造函數(shù)的實(shí)參。
所以看出賦值相對(duì)于初始化,多了一步就是使用賦值操作符進(jìn)行賦值。所以初始化的效率比賦值的效率高多了。但是對(duì)于內(nèi)置類(lèi)型,它們效率是一樣的。
二、空類(lèi)
想想你如果聲明一個(gè)空類(lèi),C++編譯器會(huì)對(duì)它做什么呢?編譯器就會(huì)為它聲明一個(gè)復(fù)制構(gòu)造函數(shù),賦值操作符和一個(gè)析構(gòu)函數(shù),以及默認(rèn)構(gòu)造函數(shù)。所有這些函數(shù)都是public而且inline函數(shù)。
編譯器寫(xiě)的賦值構(gòu)造函數(shù)和賦值操作符,只是單純地將來(lái)源對(duì)象的每個(gè)non-static變量拷貝到目標(biāo)對(duì)象,具體是進(jìn)行位拷貝。
如果聲明了一個(gè)構(gòu)造函數(shù),編譯器是不會(huì)創(chuàng)建默認(rèn)構(gòu)造函數(shù)。
如果不希望類(lèi)支持拷貝構(gòu)造函數(shù)與賦值操作符怎么辦?不聲明?按照上面說(shuō)明編譯器會(huì)自動(dòng)幫你生成。那么可以將它們聲明為private,這樣阻止編譯器自動(dòng)生成拷貝構(gòu)造函數(shù)(public)。private成功阻止他人使用,但是這并不安全。因?yàn)轭?lèi)成員函數(shù)以及友元函數(shù)還是可以調(diào)用private的拷貝構(gòu)造函數(shù)和賦值操作符。
如果只在private下聲明拷貝函數(shù)和賦值操作符,在有人通過(guò)類(lèi)成員函數(shù)去以及member函數(shù)去調(diào)用它,會(huì)獲得一個(gè)連接錯(cuò)誤。那么這里能不能將錯(cuò)誤在編譯的時(shí)候體現(xiàn)出來(lái)呢?這里只用將拷貝函數(shù)聲明為private,并且不在自身,就可以辦到了。顯然繼承一個(gè)拷貝函數(shù)和賦值操作符為private的基類(lèi)就辦到了,基類(lèi)如下:
復(fù)制代碼 代碼如下:
class NonCopyable{
protected:
NonCopyable (){}
~ NonCopyable (){}
private:
NonCopyable (const NonCopyable &);
NonCopyable & operater=(const NonCopyable &);
};
原因是類(lèi)成員函數(shù)或者友元函數(shù)嘗試拷貝對(duì)象,編譯器便會(huì)嘗試生成一個(gè)復(fù)制構(gòu)造函數(shù)與賦值操作符,并會(huì)調(diào)用基類(lèi)的對(duì)應(yīng)函數(shù),但是會(huì)被拒絕,因?yàn)榛?lèi)這些函數(shù)是private。
3、++函數(shù)
下面說(shuō)說(shuō)“*++"與"++*"中你不知道的事情,c++規(guī)定后綴形式自加函數(shù)有一個(gè)int類(lèi)型參數(shù),當(dāng)函數(shù)被調(diào)用時(shí),便其一傳遞一個(gè)0作為int參數(shù)的值傳遞給該函數(shù),而前綴形式自己函數(shù),類(lèi)型參數(shù)沒(méi)有要求,所以這樣就能區(qū)分一個(gè)++函數(shù)是前綴形式與后綴形式了,具體代碼如下:
復(fù)制代碼 代碼如下:
class UPInt{
public
UPInt& operator++( ) ; //++ 前綴
const UPInt operator++( int ); //++后綴
UPInt& operator --( ); // --前綴
const UPInt operator --( int ) //--后綴
UPInt& operator +=( int ); //
...
};
UPInt & UPInt::operator++( )
{
*this += 1;
return *this;
}
const UPInt UPInt :: operator++( int )
{
UPInt oldValue = *this;
++(*this);
return oldValue;
}
后綴函數(shù)使用返回參數(shù)類(lèi)型const,是為了避免下面代碼生效
復(fù)制代碼 代碼如下:
UPInt i;
i++++;
這個(gè)時(shí)候第一次調(diào)用++返回cosnt對(duì)象,并再次調(diào)用然后這個(gè)函數(shù)是non-const成員函數(shù),所以const對(duì)象無(wú)法調(diào)用這個(gè)函數(shù),那么i++++就無(wú)法生效了。
這里說(shuō)說(shuō)效率問(wèn)題,我們可以看到后綴++函數(shù)建立一個(gè)臨時(shí)對(duì)象以作為它返回值,這個(gè)臨時(shí)對(duì)象經(jīng)過(guò)構(gòu)造并在最后被析構(gòu)。而前綴++函數(shù)沒(méi)有這樣的臨時(shí)變量,并且沒(méi)有那樣的操作。所以如果我們?cè)诔绦蛑惺褂们熬Y++效率會(huì)更加高一些,沒(méi)有了臨時(shí)變量的構(gòu)造與析構(gòu)的動(dòng)作。
4.虛析構(gòu)函數(shù)
帶有多態(tài)性質(zhì)的base class應(yīng)該聲明一個(gè)virtual析構(gòu)函數(shù)。
為什么這么說(shuō)呢?看下面例子
復(fù)制代碼 代碼如下:
class base
{ ... }
class derived:public base
{... }
base * p= new derived;
假設(shè)這里基類(lèi)的析構(gòu)函數(shù)不是virtual,當(dāng)使用完p指針,我們刪除它的時(shí)候,想想會(huì)發(fā)生什么,因?yàn)榛?lèi)的析構(gòu)函數(shù)是non-virtual所以不會(huì)發(fā)生多態(tài)直接調(diào)用基類(lèi)析構(gòu)函數(shù),僅僅刪除繼承類(lèi)中基類(lèi)那部分內(nèi)容,那么繼承類(lèi)對(duì)象其他內(nèi)存沒(méi)有被銷(xiāo)毀,從而資源泄漏。
如果將其聲明為virtual,那么就會(huì)發(fā)生多態(tài),調(diào)用的是指向繼承類(lèi)的指針,那么就會(huì)銷(xiāo)毀的是整個(gè)繼承類(lèi)象。
5.傳遞方式用引用
缺省情況下c++以值傳遞方式傳遞對(duì)象至函數(shù)。函數(shù)參數(shù)都是以實(shí)際實(shí)參的復(fù)件為初值,而調(diào)用端所獲得的是函數(shù)返回值的一個(gè)附件。這些復(fù)件都是由拷貝構(gòu)造函數(shù)產(chǎn)出??慈缦吕?
復(fù)制代碼 代碼如下:
class Person{
public:
Person();
virtual ~Person();
...
private:
std::string name;
std::string address;
}
class Student:public Person{
public:
Student();
~Student();
...
private:
std::string schoolName;
std::string schoolAddress;
};
那么如果有一個(gè)函數(shù)驗(yàn)證是否為學(xué)生
復(fù)制代碼 代碼如下:
bool validateStudent(Student s);
Student plato;
bool platoIsOK=validateStudent(plato);
分析這3行代碼,編譯器到底做了什么?首先調(diào)用Student的copy構(gòu)造函數(shù),然后以plato為藍(lán)本將s初始化,當(dāng)validateStudent返回被銷(xiāo)毀,所以成本為"一次Student copy構(gòu)造函數(shù)調(diào)用,加上一次Student析構(gòu)函數(shù)調(diào)用"。
Student對(duì)象內(nèi)部有兩個(gè)string對(duì)象,所以構(gòu)造了兩個(gè)string對(duì)象。Student繼承自Person對(duì)象,里面又有兩個(gè)string對(duì)象。所以by value方式傳遞一個(gè)Student對(duì)象,總體成本是"六次構(gòu)造函數(shù)和六次析構(gòu)函數(shù)"!
以by reference方式傳遞參數(shù)也可避免對(duì)象切割問(wèn)題。當(dāng)一個(gè)derived class對(duì)象以by value方式傳遞并被視為一個(gè)base class對(duì)象,base class的copy構(gòu)造函數(shù)會(huì)被調(diào)用,造成像derived class對(duì)象全被切割掉了,僅僅留下base class對(duì)象。看如下代碼通過(guò)傳遞引用參數(shù)完成多態(tài)
復(fù)制代碼 代碼如下:
class Window{
public:
...
std::string name() const;
virtual void display() const;
};
class WindowWithScrollBars:public Window{
public:
...
virtual void display() const;
};
//傳入Windos類(lèi)型,調(diào)用其display函數(shù)
//傳入WindowWithScrollBars類(lèi)型,調(diào)用其display函數(shù)
//體現(xiàn)多態(tài)
void printNameAndDispaly(const Window& w)
{
std::cout<<w.name();
w.display();
}
窺視c++編譯器的底層,reference往往以指針實(shí)現(xiàn)出來(lái),因此pass by reference真正傳遞的是指針。如果對(duì)象屬于內(nèi)置型,pass by value往往比pass by reference 效率高些。
相關(guān)文章
C++ Boost Lockfree超詳細(xì)講解使用方法
Boost是為C++語(yǔ)言標(biāo)準(zhǔn)庫(kù)提供擴(kuò)展的一些C++程序庫(kù)的總稱(chēng)。Boost庫(kù)是一個(gè)可移植、提供源代碼的C++庫(kù),作為標(biāo)準(zhǔn)庫(kù)的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開(kāi)發(fā)引擎之一,是為C++語(yǔ)言標(biāo)準(zhǔn)庫(kù)提供擴(kuò)展的一些C++程序庫(kù)的總稱(chēng)2022-11-11c語(yǔ)言實(shí)現(xiàn)24小時(shí)制轉(zhuǎn)換為12小時(shí)制示例
這篇文章主要介紹了c語(yǔ)言實(shí)現(xiàn)24小時(shí)制轉(zhuǎn)換為12小時(shí)制示例,需要的朋友可以參考下2014-04-04C語(yǔ)言實(shí)現(xiàn)猜數(shù)字游戲的兩種方法
猜數(shù)字小游戲是我們大多數(shù)人學(xué)習(xí)C語(yǔ)言時(shí)都會(huì)了解到的一個(gè)有趣的C語(yǔ)言小游戲,本文就詳細(xì)的介紹一下,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01C++中關(guān)鍵字 override 的簡(jiǎn)析
這篇小文來(lái)聊聊 C++中的關(guān)鍵字 override,它的含義其實(shí)兩句話(huà)就說(shuō)完了,但為了敘述的完整性,讓我們從虛函數(shù)說(shuō)起。感興趣的小伙伴可以跟著小編一起學(xué)習(xí)下面文章內(nèi)容2021-09-09C語(yǔ)言實(shí)現(xiàn)三子棋游戲含完整代碼
本文詳細(xì)講解了C語(yǔ)言實(shí)現(xiàn)三子棋游戲內(nèi)含完整代碼,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-11-11c語(yǔ)言同名標(biāo)靶點(diǎn)自動(dòng)匹配算法實(shí)現(xiàn)實(shí)例代碼
這篇文章主要介紹了c語(yǔ)言同名標(biāo)靶點(diǎn)自動(dòng)匹配算法實(shí)現(xiàn)實(shí)例代碼,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02