C++ 高质量程序设计指南读书笔记
第四章 入門
1,全局變量的初始化不要依賴另一個(gè)全局變量。因?yàn)闊o(wú)法判斷順序。
2,每一個(gè)源代碼文件就是一個(gè)最小的編譯單元,每一個(gè)編譯單元都能獨(dú)立編譯而不需要知道其他編譯單元的存在及其編譯結(jié)果。好處:公開(kāi)接口、隱藏實(shí)現(xiàn),減少代碼修改后重新編譯的時(shí)間。
3,一個(gè)低級(jí)數(shù)據(jù)類型對(duì)象總是優(yōu)先轉(zhuǎn)換為能夠容納的下它的最大值的、占用內(nèi)存最少的高級(jí)類型對(duì)象。
4,for循環(huán)中,如果計(jì)數(shù)器從0開(kāi)始計(jì)數(shù),則建議for語(yǔ)句的循環(huán)控制變量的取值采用前閉后開(kāi)區(qū)間,這樣更直觀。for(int i = 0;I < N;i++)循環(huán)N次。
5,for循環(huán)遍歷多維數(shù)組。C++中二維數(shù)組以先行后列存儲(chǔ)在連續(xù)內(nèi)存中,先列后行效率高,因?yàn)橥鈱友h(huán)走過(guò)每一行,在走到特定行時(shí),內(nèi)層循環(huán)走過(guò)該行包含的每一列的所有元素,顯然內(nèi)層循環(huán)執(zhí)行時(shí)地址跳躍的幅度很小,都是連續(xù)地依次訪問(wèn)每一個(gè)內(nèi)存位置相鄰的元素,沒(méi)有跨行存取。
第五章 常量
1,常量分為#define定義的宏常量和const定義的常量。#define是預(yù)編譯偽指令,在編譯階段前就替換為所代表的字面常量了,因此宏常量在本質(zhì)上是字面常量。C語(yǔ)言中,const常量是值不能修改的變量,有存儲(chǔ)空間。但是C++中,基本數(shù)據(jù)類型的const常量在符號(hào)表中,不分配存儲(chǔ)空間,ADT/UDT的const對(duì)象會(huì)分配存儲(chǔ)空間。const常量有數(shù)據(jù)類型宏常量沒(méi)有數(shù)據(jù)類型,編譯器會(huì)對(duì)const常量進(jìn)行類型檢查,不會(huì)對(duì)宏常量進(jìn)行類型檢查,const常量可以調(diào)試,宏常量不可調(diào)試。
2,在C語(yǔ)言中,const常量是外連接的,也就是不能在兩個(gè)編譯單元中同時(shí)定義一個(gè)同名的const常量,或者把一個(gè)const常量定義在一個(gè)頭文件而在多個(gè)編譯單元同時(shí)包含該頭文件。但在C++中,const常量是內(nèi)連接的,編譯器會(huì)認(rèn)為它們是不同的符號(hào)常量,為每個(gè)編譯單元分別分配存儲(chǔ)空間。不要在頭文件中初始化字符串常量,這樣包含了這個(gè)頭文件的每一個(gè)編譯單元會(huì)為那一長(zhǎng)串的字符串字面常量創(chuàng)建一個(gè)獨(dú)立的拷貝項(xiàng),空間開(kāi)銷很大。
3,常量分為:#define常量,const常量,enum常量。
4,類中的cosnt: const數(shù)據(jù)成員?只在某個(gè)對(duì)象生存期內(nèi)是常量,而對(duì)于整個(gè)類而言卻是可變的。因?yàn)轭惪梢詣?chuàng)建多個(gè)對(duì)象,不同的對(duì)象其const數(shù)據(jù)成員的值可以不同。所以不能在類的聲明中初始化const數(shù)據(jù)成員,因?yàn)轭惖膶?duì)象沒(méi)被創(chuàng)建時(shí),編譯器不知道const數(shù)據(jù)成員的值是什么。const常量只能在初始化列表初始化。但是static const常量可以直接在頭文件初始化。
第六章 函數(shù)
1,函數(shù)調(diào)用方式有三種:1,過(guò)程式調(diào)用,2,嵌套調(diào)用,如;lcm(lcm(a,b),c).3,遞歸調(diào)用:自己調(diào)自己。
2,參數(shù)傳遞方式:1,值傳遞 2,地址傳遞 3,引用傳遞。如果是指針傳遞且僅作為輸入用,則應(yīng)在類型前加const,防止指針指向的內(nèi)存單元被無(wú)意修改。如果是以值傳遞的方式傳遞對(duì)象,則宜改用const &來(lái)傳遞,這樣不會(huì)調(diào)用對(duì)象的構(gòu)造和析構(gòu)函數(shù),提高效率。
3,返回值。
A, 不要省略返回值類型,如果沒(méi)有返回值,應(yīng)聲明為void類型。
B, 如果函數(shù)的返回值時(shí)一個(gè)對(duì)象,有些場(chǎng)合下可以用“返回引用”替換“返回對(duì)象值”,以提高效率,而且還可以支持鏈?zhǔn)奖磉_(dá),但有些場(chǎng)合只能返回對(duì)象值。
例如重載運(yùn)算符= 和 +,=返回引用,沒(méi)有內(nèi)容復(fù)制的過(guò)程,+返回對(duì)象值,函數(shù)內(nèi)創(chuàng)建一個(gè)臨時(shí)對(duì)象temp,如果返回引用,在函數(shù)結(jié)束后引用會(huì)失效。
C, return語(yǔ)句不可返回指向堆棧內(nèi)存的指針或引用,因?yàn)樵搩?nèi)存單元在函數(shù)體結(jié)束時(shí)會(huì)自動(dòng)釋放,如char str[] = “haha”; return str.但是const char *p=”hahaha”;return p,是對(duì)的,因?yàn)樽址A勘4嬖诔绦虻撵o態(tài)數(shù)據(jù)區(qū)。
D,return String(s1 + s2)和String result(s1 + s2);return result;不一樣:后者是先創(chuàng)建result對(duì)象,調(diào)用構(gòu)造函數(shù)初始化,再調(diào)用拷貝構(gòu)造函數(shù)把result復(fù)制到保存返回值的外部存儲(chǔ)單元,最后在函數(shù)結(jié)束時(shí)銷毀result。前者創(chuàng)建一個(gè)臨時(shí)對(duì)象并返回它,編譯器可以直接把臨時(shí)對(duì)象創(chuàng)建并初始化在外部存儲(chǔ)單元,省去了拷貝和析構(gòu)。
4,當(dāng)局部變量與某一全局變量同名時(shí),在函數(shù)內(nèi)部將遮蔽該全局變量,這時(shí)可以通過(guò)一元作用域解析運(yùn)算符(::)來(lái)引用全局變量,如,::g_iCount++。
5,任何能用遞歸函數(shù)實(shí)現(xiàn)的解決方案都可以用迭代來(lái)實(shí)現(xiàn),但是對(duì)于某些復(fù)雜的問(wèn)題,將遞歸方案展開(kāi)為迭代方案可能比較困難,而且程序的清晰性會(huì)下降。遞歸因?yàn)榉磸?fù)調(diào)用函數(shù)占用了大量的堆棧空間,運(yùn)行時(shí)開(kāi)銷很大,但是迭代只發(fā)生在一個(gè)函數(shù)內(nèi)部,反復(fù)使用局部變量進(jìn)行計(jì)算,開(kāi)銷要好很多,但是遞歸更直觀,易于閱讀和理解。
6,Use const whenever you need!
第七章 指針 數(shù)組 字符串
1,指針的算術(shù)運(yùn)算。
?????? ++,--,引用&,解引用*,==,!=。
?????? 指針加減一個(gè)正整數(shù)i,含義不是在其值上直接加減i,還要包含指針?biāo)笇?duì)象的字節(jié)數(shù)信息,如:int *pInt = new int[100];pInt +=50;編譯器改寫為pInt += 50 * sizeof(int)。所以void*類型的指針不能參與算數(shù)運(yùn)算。還有不能對(duì)voiud*類型的指針使用*來(lái)獲取它指向的變量。
2,數(shù)組不能從函數(shù)的return語(yǔ)句返回,但是數(shù)組可以作為函數(shù)的參數(shù)。數(shù)組名其實(shí)就是這個(gè)數(shù)組的首地址。把數(shù)組作為函數(shù)參數(shù)傳遞給函數(shù)時(shí)并非把整個(gè)數(shù)組的內(nèi)容傳遞進(jìn)去,此時(shí)數(shù)組會(huì)退化成一個(gè)同類型的指針。
3,字符數(shù)組是元素為字符變量的數(shù)組,字符串則是以’/0’結(jié)束字符的字符數(shù)組。
???? 如果用一個(gè)字符串字面常量來(lái)初始化一個(gè)字符數(shù)組,數(shù)組的長(zhǎng)度至少要比字符串長(zhǎng)度大1,因?yàn)檫€要保存/0.如char array[] = “Hello”;數(shù)組的元素其實(shí)是{‘H’,’e’,’l’,’l’,’o’,’’/0}。
4,引用和指針
A, 引用在創(chuàng)建的同時(shí)必須初始化,而指針在定義的時(shí)候不必初始化,可以在定義后面的任何地方重新賦值。
B, 不存在NULL引用,引用必須與合法的存儲(chǔ)單元關(guān)聯(lián),而指針可以為NULL。
C, 引用一旦被初始化為指向一個(gè)對(duì)象,它就不能被改變?yōu)榱硪粋€(gè)對(duì)象的引用,而指針可以任何時(shí)候改變?yōu)橹赶蛄硪粋€(gè)對(duì)象。
D,引用的創(chuàng)建和銷毀不會(huì)調(diào)用類的拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù)。
E,? 引用主要用途是用來(lái)修飾函數(shù)的形參和返回值,既有指針的效率,又具有變量使用的方便些和直觀性
第八章 高級(jí)數(shù)據(jù)類型、
1,數(shù)組作為函數(shù)參數(shù)時(shí),會(huì)自動(dòng)轉(zhuǎn)換為指針,但是包裝在struct/class中的數(shù)組,內(nèi)存空間完全屬于該struct/class對(duì)象。如果把struct/class對(duì)象傳遞給函數(shù)時(shí),其中的數(shù)組將全部復(fù)制到函數(shù)堆棧中,所以struct/class有數(shù)組成員時(shí),最好使用指針或者引用傳遞該對(duì)象。
第九章 編譯預(yù)處理
第十章 文件結(jié)構(gòu)和程序版式
第十一章 命名規(guī)則
第十二章 面向?qū)ο?/h2>
1,虛函數(shù)
?????? 每一個(gè)具有虛函數(shù)的類叫做多態(tài)類,C++編譯器必須為每一個(gè)多態(tài)類至少創(chuàng)建一個(gè)虛函數(shù)表vtable,它其實(shí)就是一個(gè)函數(shù)指針數(shù)組,其中存放著這個(gè)類所有的虛函數(shù)的地址及該類的類型信息。
2,override和隱藏
???? 派生類定義中的名字(對(duì)象或函數(shù)名)將義無(wú)反顧地隱藏掉基類中任何同名的對(duì)象或函數(shù)?;谶@樣的原則,如果派生類定義了一個(gè)與其基類的虛函數(shù)同名的虛函數(shù),但是參數(shù)列表有所不同,那么這就不會(huì)被編譯器認(rèn)為是對(duì)基類虛函數(shù)的改寫(Override),而是隱藏,所以也不可能發(fā)生運(yùn)行時(shí)綁定。要想達(dá)成運(yùn)行時(shí)綁定的效果,派生類和基類中同名的虛函數(shù)必須具有相同的原型。
?????? 如:
?????? class IRectangle
{
?????? Virtual ~IRectangle(){}
?????? Virtual void Draw() = 0;
}
Class RectangleImpl : public IRectangle
{
?????? …
?????? Virtual void Draw(int scale){cout << “RectangleImpl::Draw(int)”<<endl;}
?????? Virtual void Draw(){cout << “RectangleImpl::Draw()”<<endl;}
}
Void main(void)
{
?????? IRectangle *pRect = IRectangle::CreateRectangle();
?????? pRect->Draw(); //(1)
?????? pRect->Draw(200); //(2)
}
上述1調(diào)用由于pRect的靜態(tài)類型為IRectangle*,所以使用IRectangle::Draw()執(zhí)行靜態(tài)類型檢查,但由于pRect指向的對(duì)象實(shí)際是RectangleImpl對(duì)象,因此將綁定到RectangleImpl::Draw()。2調(diào)用,因?yàn)镮Rectangle沒(méi)有這個(gè)原型的函數(shù),所以拒絕編譯,除非pRect的類型為RectangleImpl*。
如果RectangleImpl不重定義Draw():
RectangleImpl* pRectImpl = new RectangleImpl;
pRectImpl->Draw(); //(3)
pRectImpl->Draw(200); //(4)
?????? 上述3調(diào)用無(wú)法編譯,因?yàn)镈raw()被Draw(int)隱藏了。
3,C++支持運(yùn)行時(shí)多態(tài)的手段有兩種,一種是虛函數(shù)機(jī)制,另一種就是RTTI。
4,多態(tài)數(shù)組
?????? 如果能夠在數(shù)組里放置一些多態(tài)對(duì)象的話,就可以通過(guò)一致的接口來(lái)動(dòng)態(tài)地調(diào)用他們自定義的虛函數(shù)實(shí)現(xiàn)了。這個(gè)想法是好的,但是會(huì)有沒(méi)意識(shí)到的問(wèn)題。如:
?????? Shape a();
?????? Circle b();
?????? Rectangle c();
Shape myShapes[3];
?????? myShapes[0] = a;
myShapes[1] = b;
myShapes[2] = c;
for(int I = 0;i<3;++i)
{
?????? myShapes[i].Draw();
}
數(shù)組不會(huì)一次調(diào)用Shape::Draw(),Circle::Draw(),Rectangle::Draw(),因?yàn)閿?shù)組的元素類型是Shape,不是Shape&或Shape*,所以會(huì)按照Shape的大小來(lái)分配內(nèi)存空間,所以三者都會(huì)調(diào)用Shape::Draw()。
所以不要在數(shù)組中直接存放多態(tài)對(duì)象,而是換之以基類指針或者基類的只能指針。
第十三章 對(duì)象的初始化 拷貝 析構(gòu)
1, 初始化列表
a)?????? 如果類存在繼承關(guān)系,派生類可以直接在初始化列表里調(diào)用基類特定的構(gòu)造函數(shù)以向它傳遞參數(shù):
Class A
{
?????? A(int x);
}
????????????? Class B : public A
????????????? {
B(int x,int y);
}
B::B(int x,int y) : A(x)
{}
b)?????? ?類的非靜態(tài)const數(shù)據(jù)成員和引用成員只能在初始化列表初始化,因?yàn)樗鼈兇嬖诔跏蓟Z(yǔ)義,而不存在復(fù)制語(yǔ)義。
c)??????? 如果一個(gè)類有另一個(gè)類的成員變量,那么在初始化列表和在構(gòu)造函數(shù)里復(fù)制的效率是不一樣的,用初始化列表直接調(diào)用另一個(gè)類的拷貝構(gòu)造函數(shù),在構(gòu)造函數(shù)里復(fù)制先調(diào)用這個(gè)類的默認(rèn)構(gòu)造函數(shù)創(chuàng)建這個(gè)對(duì)象,再調(diào)用這個(gè)類的賦值函數(shù),顯然在初始化列表初始化效率更高。
2, 拷貝構(gòu)造函數(shù) 拷貝賦值函數(shù)
如果不主動(dòng)編寫拷貝構(gòu)造函數(shù)和拷貝賦值函數(shù),編譯器將以“按成員拷貝”的方式自動(dòng)生成相應(yīng)的默認(rèn)函數(shù),如果類中有指針成員或引用成員,那這兩個(gè)默認(rèn)函數(shù)可能隱含錯(cuò)誤。如:類string的兩個(gè)對(duì)象a,b,假設(shè)a.m_data的內(nèi)容為“Hello”,b.m_data的內(nèi)容為“world”,將a賦值給b,默認(rèn)賦值函數(shù)的“按成員拷貝”意味著執(zhí)行b.m_data = a.m_data,這將造成三個(gè)錯(cuò)誤:1,b.m_data原來(lái)的內(nèi)存沒(méi)有被釋放,造成內(nèi)存泄露。2,b.m_data和a.m_data指向同一塊內(nèi)存,a或b任何一方的變動(dòng)都會(huì)影響另一方。3,在對(duì)象被析構(gòu),m_data被delete了兩次。
拷貝構(gòu)造函數(shù)是在對(duì)象被創(chuàng)建并用另一個(gè)已經(jīng)存在的對(duì)象來(lái)初始化它時(shí)調(diào)用的,而賦值函數(shù)只能把一個(gè)對(duì)象賦值給另一個(gè)已經(jīng)存在的對(duì)象:
String a(“hello”);
String b(“world”);
String c = a; //拷貝構(gòu)造函數(shù),因?yàn)閏這時(shí)才被創(chuàng)建,不過(guò)最好寫成c(a)
C = a; //賦值函數(shù),因?yàn)閏這個(gè)對(duì)象已經(jīng)存在。
3, 派生類的基本函數(shù)
1, 派生類的構(gòu)造函數(shù)應(yīng)在初始化列表顯示的調(diào)用基類的構(gòu)造函數(shù)。
2, 如果基類是多態(tài)類,那么必須把基類的析構(gòu)函數(shù)定義為虛函數(shù),這樣就可以像其他虛函數(shù)一樣實(shí)現(xiàn)動(dòng)態(tài)綁定,否則可能造成內(nèi)存泄露。
#include <stdafx.h>
#include <iostream>
class Base
{
public:
?????? Base() {}
?????? ~Base() { std::cout << "Base::~Base()" << std::endl; }
};
?
class Derived : public Base
{
public:
?????? Derived() {}
?????? ~Derived() { std::cout << "Derived::~Derived()" << std::endl; }
};
?
int main(void)
{
?????? Base* p = new Derived();
?????? delete p;
?
?????? return 0;
}
????????????? 如果基類的析構(gòu)函數(shù)不是虛函數(shù),派生類的析構(gòu)函數(shù)就不會(huì)調(diào)用,造成內(nèi)存泄漏,上述輸出為Base::~Base(),將析構(gòu)函數(shù)改為虛函數(shù)后,輸出為:Derived::~Derived()
Base::~Base().
3, 在編寫派生類的賦值函數(shù)時(shí),注意不要忘記對(duì)基類的數(shù)據(jù)成員重新賦值,可以通過(guò)調(diào)用基類的賦值函數(shù)來(lái)實(shí)現(xiàn)。如Base::operator=(other)。因?yàn)椴荒苤苯硬僮骰惖乃接谐蓡T。
4,對(duì)象的構(gòu)造和析構(gòu)次序
?????? 首先調(diào)用每一個(gè)基類的構(gòu)造函數(shù),然后調(diào)用成員對(duì)象的構(gòu)造函數(shù),而每一個(gè)基類的構(gòu)造函數(shù)又將首先調(diào)用它們各自基類的構(gòu)造函數(shù),直到最根類。所以,任何一個(gè)對(duì)象總是首先構(gòu)造最根類的子對(duì)象,然后逐層向下擴(kuò)展,直到把整個(gè)對(duì)象構(gòu)造起來(lái)。
?????? 析構(gòu)函數(shù)會(huì)嚴(yán)格按照與對(duì)象構(gòu)造相反的次序執(zhí)行,數(shù)據(jù)成員的初始化次序跟它們?cè)诔跏蓟斜碇械捻樞驔](méi)有關(guān)系,只跟它們?cè)陬愔新暶鞯捻樞蛴嘘P(guān)。顯然,如果每個(gè)構(gòu)造函數(shù)的初始化列表各成員的順序不可能完全相同,如果按照這個(gè)順序,那析構(gòu)函數(shù)就不會(huì)得到唯一的逆序了。
第十四章 函數(shù)的高級(jí)特性
1, 重置 覆蓋(override) 和隱藏
重載:具有相同的作用域、函數(shù)名字相同、參數(shù)類型、順序或數(shù)目不同。
覆蓋:派生類重新實(shí)現(xiàn)了基類的成員函數(shù)。不同的作用域,函數(shù)名稱相同,參數(shù)列表相同,基類函數(shù)是虛函數(shù)。
虛函數(shù)的覆蓋有兩種方式:完全重寫和擴(kuò)展。擴(kuò)展指派生類虛函數(shù)首先調(diào)用基類的虛函數(shù),然后再增加新的功能,完全重寫則不調(diào)。
隱藏:a)派生類的函數(shù)與基類的函數(shù)同名,但是參數(shù)列表有差異,此時(shí),不論有無(wú)virtual關(guān)鍵字,基類的函數(shù)在派生類中都將被隱藏。b)派生類的函數(shù)與基類的函數(shù)同名,參數(shù)列表也相同,但是基類函數(shù)沒(méi)有virtual關(guān)鍵字,此時(shí),基類的函數(shù)在派生類中將被隱藏。
隱藏這個(gè)玩意 哎。
2, i++ ++i
int b = ++a;?
相當(dāng)于:
a += 1;
int b = a;i
?
int b = a++;
相當(dāng)于:
Int temp = a;
a += 1;
int b = temp;
temp.~int();
3,內(nèi)聯(lián)函數(shù)
?????? 用內(nèi)聯(lián)函數(shù)替代宏:
A) 宏容易出錯(cuò),不可調(diào)試。
B) 內(nèi)聯(lián)函數(shù)可以調(diào)試,在程序的Debug版本,沒(méi)有真正的內(nèi)聯(lián),編譯器像普通函數(shù)那樣為它生成含有調(diào)試信息的可執(zhí)行代碼,在Release版本才真正內(nèi)聯(lián)。
C) 宏不能操作類的私有成員。
D)內(nèi)聯(lián)函數(shù)以代碼膨脹為代價(jià),省去了函數(shù)調(diào)用的開(kāi)銷,從而提高程序的執(zhí)行效率,如果函數(shù)體的代碼比函數(shù)調(diào)用的開(kāi)銷大得多,那inline的收益會(huì)很小。
E)? 以下情況不宜用inline:
如果函數(shù)體內(nèi)代碼過(guò)長(zhǎng),使用內(nèi)聯(lián)將導(dǎo)致代碼膨脹過(guò)大。
如果函數(shù)體內(nèi)出現(xiàn)循環(huán)或其他復(fù)雜的控制結(jié)構(gòu),那執(zhí)行函數(shù)體內(nèi)代碼的時(shí)間比函數(shù)調(diào)用的開(kāi)銷大得多,內(nèi)聯(lián)的意義不大。
4,類型轉(zhuǎn)換函數(shù)
A) 類的構(gòu)造函數(shù)
Class Point2D : public Point
{
??????? Public:
?????????????? Point2D(const Point& p);
}
Point2D的構(gòu)造函數(shù)可以將一個(gè)Point對(duì)象自動(dòng)轉(zhuǎn)換為一個(gè)Point2D對(duì)象。
B) 自定義類型轉(zhuǎn)換運(yùn)算符
類型轉(zhuǎn)換運(yùn)算符以operator關(guān)鍵字開(kāi)始,緊接著目標(biāo)類型名和()。
Class Point
{
??????? Public:
?????????????? Point(float x);
?????????????? Operator float() const
?????????????? {return m_x;}
??????? Private:
?????????????? Float m_x;
}
Point p(100.25);
Coust << p << endl;
?
C) 類型轉(zhuǎn)換運(yùn)算符
static_cast<dest_type>(src_obj)
const_cast<dest_type>(src_obj)
reinterpret_cast<dest_type>(src_obj)
dynamic_cast<dest_type>(src_obj)
5,const 成員函數(shù)
?????? 任何不會(huì)修改數(shù)據(jù)成員的成員函數(shù)都應(yīng)該聲明為const,當(dāng)編寫const成員函數(shù)時(shí)不小心寫下了試圖修改數(shù)據(jù)成員的代碼,或者調(diào)用了非const成員函數(shù),編譯器將指出錯(cuò)誤。Const跟在函數(shù)尾巴。
第十五章 異常處理和RTTI
1, RTTI
A) typeid
if(typeid(device) == typeid(Television))
{
? Television *p = static_cast<Television*>(&device);
}
B) dynamic_cast<>
Television& tv = dynamic_cast<Television&>(device);
第十六章 內(nèi)存管理
1, 內(nèi)存分配方式
A) 靜態(tài)存儲(chǔ)區(qū)域分配。內(nèi)存在程序編譯的時(shí)候就已經(jīng)分配好了,這些內(nèi)存在程序的整個(gè)運(yùn)行期間都存在,如全局變量、static變量。
B) 在堆棧分配。函數(shù)內(nèi)的局部變量創(chuàng)建在堆棧上,函數(shù)結(jié)束時(shí)這些存儲(chǔ)單元自動(dòng)釋放。
C) 堆或自由存儲(chǔ)空間分配,也叫動(dòng)態(tài)分配。使用malloc new申請(qǐng)的內(nèi)存,程序員自己掌握釋放內(nèi)存的時(shí)機(jī),用free delete。
D)原則:如果使用堆棧存儲(chǔ)和靜態(tài)存儲(chǔ)就能滿足應(yīng)用要求,就不要使用動(dòng)態(tài)存儲(chǔ)。
2, 常見(jiàn)的內(nèi)存錯(cuò)誤
A) 內(nèi)存分配未成功,卻使用它。
在使用內(nèi)存之前判斷指針是否為NULL,如果是new申請(qǐng)的內(nèi)存,用捕獲異常來(lái)處理。
B) 內(nèi)存分配成功,但是還沒(méi)有初始化就使用了它。
C) 內(nèi)存分配成功并且已經(jīng)初始化,但操作越過(guò)了內(nèi)存的邊界。
D)忘記釋放內(nèi)存或者只釋放了部分內(nèi)存。這樣會(huì)造成內(nèi)存泄露。
E)? 釋放了內(nèi)存卻還在使用它。
3, 指針參數(shù)如何傳遞內(nèi)存
A)
Void GetMemory(char*,int num)
{
??????? P = (char*)malloc(sizeof(char) * num);
}
編譯器總是為函數(shù)的每個(gè)參數(shù)制作臨時(shí)副本,指針參數(shù)p的副本是_p,編譯器使_p=p。如果函數(shù)修改了_p指向的內(nèi)容,那么p指向的內(nèi)容也就被修改了,這就是指針可以作為輸出參數(shù)的原因。但是這里,_p申請(qǐng)了內(nèi)存空間并指向它,但是p沒(méi)有指向它,所以GetMemory并不會(huì)輸出任何東西,反而會(huì)造成內(nèi)存泄露,因?yàn)闆](méi)有調(diào)用相應(yīng)的free來(lái)釋放內(nèi)存。
B)使用指向指針的指針或指針的引用來(lái)實(shí)現(xiàn)在函數(shù)中動(dòng)態(tài)分配內(nèi)存
?
Void GetMemory(char **p,int num)? //或者char *&p
{
??????? *p = (char*)malloc(sizeof(char) * num);
}
參數(shù)為指向指針的指針,編譯器會(huì)創(chuàng)建一個(gè)_p副本,跟p指向同一個(gè)指針,函數(shù)內(nèi)*_p跟*p是同一個(gè)指針,修改*_p也就修改了*p。
C) 使用返回值來(lái)傳遞動(dòng)態(tài)內(nèi)存
Char* GetMemory(int num)
{
???????? Char *p = (char*)malloc(sizeof(char) * num);
???????? Return p;
}
這里return返回的不是堆棧上的內(nèi)存,所以不會(huì)在函數(shù)結(jié)束的時(shí)候被釋放。
?????? D)
????????????? Char* GetString(void)
????????????? {
???????????????????? Char p[] = “hello world”;
???????????????????? Return p;
}
數(shù)組p在堆棧中,函數(shù)結(jié)束后會(huì)被釋放。
?????? E)
????????????? Char* GetString(void)
????????????? {
???????????????????? Char *p = “hello world”;
???????????????????? Return p;
}
字符串常量位于靜態(tài)存儲(chǔ)區(qū),在程序的整個(gè)生命周期都有效無(wú)論什么時(shí)候調(diào)用GetString返回的都只是一個(gè)只讀的內(nèi)存塊的地址,可以把返回值改為const char*防止無(wú)意中的修改。
4,malloc free是C中的庫(kù)函數(shù),不會(huì)調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù),new delete是C++的運(yùn)算符,會(huì)自動(dòng)調(diào)用類的構(gòu)造函數(shù)和析構(gòu)函數(shù)。
第十七章 STL
1,六大組件:容器,存儲(chǔ)分配器,迭代器,泛型算法,函數(shù)對(duì)象,適配器。
轉(zhuǎn)載于:https://www.cnblogs.com/litmin/p/8572657.html
總結(jié)
以上是生活随笔為你收集整理的C++ 高质量程序设计指南读书笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: zk介绍
- 下一篇: 并发的HashMap为什么会引起死循环?