C++你最好不要做的幾點(diǎn)小結(jié)
1、最好不要使用引用返回值
有同學(xué)在傳遞的參數(shù)的時(shí)候使用引用方式傳遞,避免了臨時(shí)對(duì)象的創(chuàng)建,提高了效率,那么在返回值的時(shí)候能不能使用引用呢?
看如下代碼
class Rational{
public:
Raional( int numerator = 0, int denominator =1);
...
private:
int d, d;
friend Rational operator* (const Rational& lhs, const Raional& rhs) ;
};
Rational Rational::operator* (const Rational& lhs,const Raionl&rhs)
{
return Rational result(lhs.n*rhs.n,lhs.d*rhs.d);
}
}
這個(gè)類(lèi)就是我們前面所介紹的有理數(shù)類(lèi)。這里想想會(huì)發(fā)生一次類(lèi)的構(gòu)造與類(lèi)的析構(gòu),那么如果使用引用就可以避免這個(gè)問(wèn)題嗎?達(dá)到提高效率嗎?
函數(shù)創(chuàng)建新對(duì)象有兩種方法,一個(gè)是在棧(statck)中創(chuàng)建,一個(gè)是在堆(heep)中創(chuàng)建。
People p(a,b) //棧中創(chuàng)建
People *p = new People(a,b) //堆中創(chuàng)建
現(xiàn)在首先考慮在棧中創(chuàng)建,但是這個(gè)創(chuàng)建的變量是一個(gè)局部變量,會(huì)在退出函數(shù)之前銷(xiāo)毀。
const Rational& operator* (const Rational& lhs, const Rational & rhs)
{
Rational result(lhs.n*rhs.n,lhs.d*rhs.d);
return result;
}
在函數(shù)內(nèi)以stack方式空間創(chuàng)建對(duì)象是局部對(duì)象,任何函數(shù)如果返回一個(gè)引用指向某個(gè)局部對(duì)象,都會(huì)發(fā)生錯(cuò)誤。因?yàn)榫植繉?duì)象在函數(shù)退出之前被銷(xiāo)毀了,意味著reference所指的對(duì)象不存在。
于是考慮在堆中創(chuàng)建
const Rational& operator* (const Rational& lhs, const Rational & rhs)
{
Rational* result=new Rational(lhs.n*rhs.n,lhs.d*rhs.d);
return *result;
}
現(xiàn)在又發(fā)現(xiàn)了一個(gè)問(wèn)題,new出來(lái)的對(duì)象由誰(shuí)來(lái)delete?好這個(gè)問(wèn)題先占時(shí)不考慮看下面情況
Rational w,x,y,z;
w=x*y*z;
這里同時(shí)一個(gè)語(yǔ)句調(diào)用了兩次operator*,意味著new了兩次,也就需要delete兩次。但是這里沒(méi)有合理的辦法讓opertaor*使用者進(jìn)行那些delete調(diào)用,因?yàn)闊o(wú)法讓使用者獲取返回的指針,這將導(dǎo)致資源泄漏。
于是考慮返回一個(gè)引用,其指向定義于函數(shù)內(nèi)部的static Rational對(duì)象。
const Rational & operator*(const Rational& lhs,const Rational & rhs)
{
static Rational result;
result = ...;
return result;
}
那么顯而易見(jiàn)就是多線程,在多線程環(huán)境下,這樣寫(xiě)安全嗎?好如果說(shuō)不關(guān)多線程。那么如下代碼會(huì)發(fā)生什么?
bool operator == (const Rational& lhs, const Rational& rhs);
...
Raional a,b,c,d;
if((a*b) == (c*d)
{
...
}
上述if語(yǔ)句表達(dá)式無(wú)論a,b,c,d為何值都是true,因?yàn)樗鼈兌贾赶蛲粋€(gè)靜態(tài)值。
2、最好不要將所有變量定義放在語(yǔ)句開(kāi)頭。
有同學(xué)可能上過(guò)C語(yǔ)言課程,喜歡學(xué)習(xí)C的,喜歡將所有的變量定義放在開(kāi)頭,但是在C++中,我建議最好不要這樣做,因?yàn)槎x一個(gè)變量時(shí),程序便注定需要進(jìn)行一次構(gòu)造與析構(gòu)。例如在下面程序:大概意思我們?cè)试S1米8以下并且年齡在60歲以下的同學(xué)買(mǎi)票進(jìn)入。
class People{...};
class Ticket{...};
bool Isvalid(const People&p){...}
void Banding(const People& p,Ticket& t);
Ticket buyTicket(const People& p)
{
Ticket t;
if(Isvalid(p)){ return NULL };
//信息與票綁定
Banding(p,&t);
return t;
}
假如這里檢測(cè)買(mǎi)票人條件不符合,那么就不能進(jìn)入買(mǎi)票從而進(jìn)行信息與綁定操作,那么這里Ticket t語(yǔ)句就讓該函數(shù)白白承受了一次Ticket構(gòu)造成本與析構(gòu)的成本。
所以最好不要將變量提前定義,最好在要用到的時(shí)候定義,避免不必要的性能開(kāi)銷(xiāo)。上面例子改成下面這樣即可:
class People{...};
class Ticket{...};
bool Isvalid(const People&p){...}
void Banding(const People& p,Ticket& t);
Ticket buyTicket(const People& p)
{
if(Isvalid(p)){ return NULL };
Ticket t;
//信息與票綁定
Banding(p,&t);
return t;
}
3、最好不要做過(guò)多的類(lèi)型轉(zhuǎn)換
C++規(guī)則的設(shè)計(jì)目標(biāo)之一是,保證“類(lèi)型錯(cuò)誤”絕不可能發(fā)生。理論上程序通過(guò)編譯,就表示它并不企圖在任何身上執(zhí)行任何不安全,荒謬的操作??上ь?lèi)型轉(zhuǎn)換破環(huán)了類(lèi)型系統(tǒng),它可能導(dǎo)致任何種類(lèi)麻煩,有些非常麻煩。就例如本文最后一個(gè)代碼例子。C和C++都支持隱形類(lèi)型轉(zhuǎn)換,同時(shí)C++有四種顯示轉(zhuǎn)換操作符。成員函數(shù)與非成員函數(shù)的抉擇里有介紹。但是建議最好不要做過(guò)多的類(lèi)型轉(zhuǎn)換,能避免就避免。類(lèi)型轉(zhuǎn)換往往也不是按照你的意思,首先看一個(gè)例子:
#include <iostream>
class base
{
public:
base():a(0),b(0){}
base(const int& x,const int& y)
:a(x),b(y){}
virtual void init()
{
a=5;
b=5;
std::cout<<"in base a value is "<<a<<std::endl;
std::cout<<"in base b value is "<<b<<std::endl;
}
int get_a() const
{
return a;
}
int get_b() const
{
return b;
}
private:
int a;
int b;
};
class derived:public base
{
public:
derived(int x,int y):base(x,y){}
void init()
{
static_cast<base>(*this).init();
}
};
運(yùn)行結(jié)果為
in base a value is 5
in base b value is 5
a value is 2
b value is 2
這里將derived類(lèi)型轉(zhuǎn)化為base,但是調(diào)用base::init()函數(shù)并不是當(dāng)前對(duì)象上的函數(shù),而是早前轉(zhuǎn)型動(dòng)作所建立的一個(gè)"*this對(duì)象的base的副本,所以當(dāng)我們嘗試改變對(duì)象內(nèi)容,其實(shí)改變的是副本內(nèi)容,其對(duì)象內(nèi)容并沒(méi)有被改變。
如何解決這個(gè)問(wèn)題呢?我們可以直接聲明調(diào)用基類(lèi)的函數(shù)
class derived:public base
{
public:
derived(int x,int y):base(x,y){}
void init()
{
//static_cast<base>(*this).init();
base::init();
}
};
運(yùn)行結(jié)果為:
in base a value is 5
in base b value is 5
a value is 5
b value is 5
或許此時(shí)你記起來(lái)應(yīng)該使用dynamic_case(如果看過(guò)以前的文章的話:它用于安全地沿著繼承關(guān)系向下進(jìn)行類(lèi)型轉(zhuǎn)換)。使用dynamic_cast直接出現(xiàn)錯(cuò)誤。
class derived:public base
{
public:
derived(int x,int y):base(x,y){}
void init()
{
//static_cast<base>(*this).init();
//base::init();
dynamic_cast<base*>(this)->init();
}
};
運(yùn)行結(jié)果為:
段錯(cuò)誤 ((主存儲(chǔ)器)信息轉(zhuǎn)儲(chǔ))假設(shè)一個(gè)類(lèi)有五層的單繼承關(guān)系,如果在該對(duì)象上執(zhí)行dynaic_cast,那么會(huì)有多達(dá)五次的strcmp調(diào)用,深度或者多重繼承的越多,成本越高。之所以需要dynamic_cast是因?yàn)橄朐赿erived class對(duì)象上執(zhí)行 derived class操作函數(shù),但是目前只有一個(gè)指向base的指針或者引用,這個(gè)時(shí)候可以用它們來(lái)處理。
相關(guān)文章
Matlab實(shí)現(xiàn)三維投影繪制的示例代碼
這篇文章系小編為大家?guī)?lái)了一個(gè)三維投影繪制函數(shù)(三視圖繪制),函數(shù)支持三維曲線、曲面、三維多邊形、參數(shù)方程曲線、參數(shù)方程曲面的投影繪制,需要的可以參考一下2022-08-08C++實(shí)現(xiàn)LeetCode(129.求根到葉節(jié)點(diǎn)數(shù)字之和)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(129.求根到葉節(jié)點(diǎn)數(shù)字之和),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07C語(yǔ)言鏈表實(shí)現(xiàn)圖書(shū)管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言鏈表實(shí)現(xiàn)圖書(shū)管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01Visual?Studio?2022使用MinGW來(lái)編譯調(diào)試C/C++程序的圖文教程
這篇文章主要介紹了Visual?Studio?2022使用MinGW來(lái)編譯調(diào)試C/C++程序,以實(shí)例來(lái)簡(jiǎn)單介紹一下VS2022中如何使用MinGW來(lái)編譯、調(diào)試C/C++程序,需要的朋友可以參考下2022-08-08