C++入門基礎(chǔ)
namespace專題講座
namespace概念
所謂namespace,是指標(biāo)識符的各種可見范圍。Cpp標(biāo)準(zhǔn)程序庫中的所有標(biāo)識符都被定義于一個名為std的namespace中放坏。
- <iostream>和<iostream.h>格式不一樣玫荣,前者沒有后綴璧针,實際上坦袍,在你的編譯器include文件夾里面可以看到十厢,二者是兩個文件,打開文件就會發(fā)現(xiàn)捂齐,里面的代碼是不一樣的蛮放。后綴為.h的頭文件cpp標(biāo)準(zhǔn)已經(jīng)明確提出不支持了,早些的實現(xiàn)將標(biāo)準(zhǔn)庫功能定義在全局空間里奠宜,聲明在帶.h后綴的頭文件里包颁,cpp標(biāo)準(zhǔn)為了和C區(qū)別開,也為了正確使用命名空間压真,規(guī)定頭文件不使用后綴.h娩嚼。 因此,
* 當(dāng)使用<iostream.h>時滴肿,相當(dāng)于在c中調(diào)用庫函數(shù)待锈,使用的是全局命名空間,也就是早期的cpp實現(xiàn)嘴高;
* 當(dāng)使用<iostream>的時候竿音,該頭文件沒有定義全局命名空間,必須使用namespace std拴驮;這樣才能正確使用cout春瞬。
- 由于namespace的概念,使用Cpp標(biāo)準(zhǔn)程序庫的任何標(biāo)識符時套啤,可以有三種選擇:
- 直接指定標(biāo)識符宽气。例如std::ostream而不是ostream。完整語句如下: std::cout << std::hex << 3.4 << std::endl;
- 使用using關(guān)鍵字潜沦。 using std::cout; using std::endl; using std::cin; 以上程序可以寫成 cout << std::hex << 3.4 << endl;
- 最方便的就是使用using namespace std; 例如: using namespace std;這樣命名空間std內(nèi)定義的所有標(biāo)識符都有效(曝光)萄涯。就好像它們被聲明為全局變量一樣。那么以上語句可以如下寫: cout <<hex << 3.4 << endl;因為標(biāo)準(zhǔn)庫非常的龐大唆鸡,所以程序員在選擇的類的名稱或函數(shù)名 時就很有可能和標(biāo)準(zhǔn)庫中的某個名字相同涝影。所以為了避免這種情況所造成的名字沖突,就把標(biāo)準(zhǔn)庫中的一切都被放在名字空間std中争占。但這又會帶來了一個新問 題燃逻。無數(shù)原有的Cpp代碼都依賴于使用了多年的偽標(biāo)準(zhǔn)庫中的功能,他們都是在全局空間下的臂痕。所以就有了<iostream.h> 和<iostream>等等這樣的頭文件伯襟,一個是為了兼容以前的Cpp代碼,一個是為了支持新的標(biāo)準(zhǔn)握童。命名空間std封裝的是標(biāo)準(zhǔn)程序庫的名稱姆怪,標(biāo)準(zhǔn)程序庫為了和以前的頭文件區(qū)別,一般不加".h"
cpp命名空間定義及使用語法
在Cpp中,名稱(name)可以是符號常量稽揭、變量红伦、宏、函數(shù)淀衣、結(jié)構(gòu)、枚舉召调、類和對象等等膨桥。為了避免,
在大規(guī)模程序的設(shè)計中唠叛,以及在程序員使用各種各樣的Cpp庫時只嚣,這些標(biāo)識符的命名發(fā)生沖突,
標(biāo)準(zhǔn)Cpp引入了關(guān)鍵字namespace(命名空間/名字空間/名稱空間/名域)艺沼,可以更好地控制標(biāo)識符的作用域册舞。std是cpp標(biāo)準(zhǔn)命名空間,cpp標(biāo)準(zhǔn)程序庫中的所有標(biāo)識符都被定義在std中障般,比如標(biāo)準(zhǔn)庫中的類iostream调鲸、vector等都定義在該命名空間中,使用時要加上using聲明(using namespace std) 或using指示(如std::string挽荡、std::vector<int>).::
C中的命名空間
在C語言中只有一個全局作用域
C語言中所有的全局標(biāo)識符共享同一個作用域
標(biāo)識符之間可能發(fā)生沖突
Cpp中提出了命名空間的概念
命名空間將全局作用域分成不同的部分
不同命名空間中的標(biāo)識符可以同名而不會發(fā)生沖突
命名空間可以相互嵌套
全局作用域也叫默認命名空間
Cpp命名空間的定義:
namespace name { … }
- Cpp命名空間的使用:
使用整個命名空間:using namespace name;
使用命名空間中的變量:using name::variable;
使用默認命名空間中的變量:::variable
默認情況下可以直接使用默 認命名空間中的所有標(biāo)識符
cpp命名空間實踐
#include "iostream"
namespace NamespaceA
{
int a = 0;
}
namespace NamespaceB
{
int a = 1;
namespace NamespaceC
{
struct Teacher{
char name[68];
int age;
};
}
}
int main(){
using namespace NamespaceA;
using namespace NamespaceB;
printf("namespaceA:%d \n",NamespaceA::a);
printf("namespaceB:%d \n",NamespaceB::a);
NamespaceB::NamespaceC::Teacher t = {"abc",1};
printf("t.name:%s \n",t.name);
printf("t.age:%d \n",t.age);
return 0;
}
打印結(jié)果如下:
namespaceA:0
namespaceB:1
t.name:abc
t.age:1
結(jié)論
-
當(dāng)使用<iostream>的時候藐石,該頭文件沒有定義全局命名空間,必須使用namespace std定拟;這樣才能正確使用cout于微。若不引入using namespace std ,需要這樣做。std::cout青自。
using namespace std; int main(){ cout<<"hello world!"<<endl; return 0; }
c++標(biāo)準(zhǔn)為了和C區(qū)別開株依,也為了正確使用命名空間,規(guī)定頭文件不使用后綴.h延窜。
C++命名空間的定義: namespace name { … }
using namespace NameSpaceA;
namespce定義可嵌套恋腕。
C++中的語法增強
實用性增強
C語言中的變量都必須在作用域開始的位置定義!逆瑞!C++中更強調(diào)語言的“實用性”吗坚,所有的變量都可以在需要使用時再定義。
int main(){
int a = 0;
printf("a:%d \n",a);
int k;
return 0;
}
register關(guān)鍵字增強
register關(guān)鍵字 請求編譯器讓變量a直接放在寄存器里面呆万,速度快(不通過內(nèi)存來存取變量商源,內(nèi)存和CPU中來回運作速度會降低)
在C語言中 register修飾的變量 不能取地址,但是在c++里面做了內(nèi)容增強:
register關(guān)鍵字的變化
register關(guān)鍵字請求“編譯器”將局部變量存儲于寄存器中
C語言中無法取得register變量地址
在C++中依然支持register關(guān)鍵字
C++編譯器有自己的優(yōu)化方式谋减,不使用register也可能做優(yōu)化
C++中可以取得register變量的地址C++編譯器發(fā)現(xiàn)程序中需要取register變量的地址時牡彻,register對變量的聲明變得無效。
早期C語言編譯器不會對代碼進行優(yōu)化,因此register變量是一個很好的補充庄吼。
int main(){
register int a = 0;
printf("&a = %p \n",&a);
return 0;
}
//打印結(jié)果 &a = 0x7fff586d7698
函數(shù)檢測增強
在C語言中缎除,重復(fù)定義多個同名的全局變量是合法的。但在C++中总寻,不允許定義多個同名的全局變量器罐。C語言中多個同名的全局變量最終會被鏈接到全局數(shù)據(jù)區(qū)的同一個地址空間上
C++直接拒絕這種二義性的做法。
int g_var;
int g_var = 1;
//err: previous definition is here
struct類型加強
C語言的struct定義了一組變量的集合渐行,C編譯器并不認為這是一種新的類型
C++中的struct是一個新類型的定義聲明
#include "iostream"
using namespace std;
struct Student{
char name[64];
int age;
};
int main(){
Student s1 = {"小紅",18};
Student s2 = {"小明",17};
return 0;
}
C++中所有的變量和函數(shù)都必須有類型
C++中所有的變量和函數(shù)都必須有類型
C語言中的默認類型在C++中是不合法的
函數(shù)f的返回值是什么類型轰坊,參數(shù)又是什么類型?
函數(shù)g可以接受多少個參數(shù)祟印?
void f(int i){
printf("i = %d \n",i);
}
//該代碼在C中可以正常編譯通過肴沫,但是在C++中會報錯
g(){
return 10;
}
int main(){
f(1);
return 0;
}
總結(jié):
在C語言中:
int f( );表示返回值為int蕴忆,接受任意參數(shù)的函數(shù)
int f(void)颤芬;表示返回值為int的無參函數(shù)
在C++中:
int f( )和int f(void)具有相同的意義,都表示返回值為int的無參函數(shù)
C++更加強調(diào)類型套鹅,任意的程序元素都必須顯示指明類型
一站蝠、C語言register關(guān)鍵字—最快的關(guān)鍵字
register:這個關(guān)鍵字請求編譯器盡可能的將變量存在CPU內(nèi)部寄存器中,而不是通過內(nèi)存尋址訪問卓鹿,以提高效率沉衣。注意是盡可能,不是絕對减牺。你想想豌习,一個CPU 的寄存器也就那么幾個或幾十個,你要是定義了很多很多register 變量拔疚,它累死也可能不能全部把這些變量放入寄存器吧肥隆,輪也可能輪不到你。
1. 皇帝身邊的小太監(jiān)----寄存器
??不知道什么是寄存器稚失?那見過太監(jiān)沒有栋艳?沒有?其實我也沒有句各。沒見過不要緊吸占,見過就麻煩大了。大家都看過古裝戲凿宾,那些皇帝們要閱讀奏章的時候矾屯,大臣總是先將奏章交給皇帝旁邊的小太監(jiān),小太監(jiān)呢再交給皇帝同志處理初厚。這個小太監(jiān)只是個中轉(zhuǎn)站件蚕,并無別的功能。
??好,那我們再聯(lián)想到我們的CPU排作。CPU 不就是我們的皇帝同志么牵啦?大臣就相當(dāng)于我們的內(nèi)存,數(shù)據(jù)從他這拿出來妄痪。那小太監(jiān)就是我們的寄存器了(這里先不考慮CPU 的高速緩存區(qū))哈雏。數(shù)據(jù)從內(nèi)存里拿出來先放到寄存器,然后CPU 再從寄存器里讀取數(shù)據(jù)來處理衫生,處理完后同樣把數(shù)據(jù)通過寄存器存放到內(nèi)存里裳瘪,CPU 不直接和內(nèi)存打交道。這里要說明的一點是:小太監(jiān)是主動的從大臣手里接過奏章障簿,然后主動的交給皇帝同志,但寄存器沒這么自覺栅迄,它從不主動干什么事站故。一個皇帝可能有好些小太監(jiān),那么一個CPU 也可以有很多寄存器毅舆,不同型號的CPU 擁有寄存器的數(shù)量不一樣西篓。
??為啥要這么麻煩啊憋活?速度岂津!就是因為速度。寄存器其實就是一塊一塊小的存儲空間悦即,只不過其存取速度要比內(nèi)存快得多吮成。進水樓臺先得月嘛,它離CPU 很近辜梳,CPU 一伸手就拿到數(shù)據(jù)了粱甫,比在那么大的一塊內(nèi)存里去尋找某個地址上的數(shù)據(jù)是不是快多了?那有人問既然它速度那么快作瞄,那我們的內(nèi)存硬盤都改成寄存器得了唄茶宵。我要說的是:你真有錢!
2.舉例
??register修飾符暗示編譯程序相應(yīng)的變量將被頻繁地使用宗挥,如果可能的話乌庶,應(yīng)將其保存在CPU的寄存器中,以加快其存儲速度契耿。例如下面的內(nèi)存塊拷貝代碼:
#ifdef NOSTRUCTASSIGN
memcpy (d, s, l)
{
register char *d;
register char *s;
register int i;
while (i--)
*d++ = *s++;
}
#endif
3.使用register 修飾符的注意點
??但是使用register修飾符有幾點限制瞒大。
??首先,register變量必須是能被CPU所接受的類型搪桂。這通常意味著register變量必須是一個單個的值糠赦,并且長度應(yīng)該小于或者等于整型的長度。不過,有些機器的寄存器也能存放浮點數(shù)拙泽。
??其次淌山,因為register變量可能不存放在內(nèi)存中,所以不能用“&”來獲取register變量的地址顾瞻。
??由于寄存器的數(shù)量有限泼疑,而且某些寄存器只能接受特定類型的數(shù)據(jù)(如指針和浮點數(shù)),因此真正起作用的register修飾符的數(shù)目和類型都依賴于運行程序的機器荷荤,而任何多余的register修飾符都將被編譯程序所忽略退渗。
??在某些情況下,把變量保存在寄存器中反而會降低程序的運行速度蕴纳。因為被占用的寄存器不能再用于其它目的会油;或者變量被使用的次數(shù)不夠多,不足以裝入和存儲變量所帶來的額外開銷古毛。
??早期的C編譯程序不會把變量保存在寄存器中翻翩,除非你命令它這樣做,這時register修飾符是C語言的一種很有價值的補充稻薇。然而嫂冻,隨著編譯程序設(shè)計技術(shù)的進步,在決定那些變量應(yīng)該被存到寄存器中時塞椎,現(xiàn)在的C編譯環(huán)境能比程序員做出更好的決定桨仿。實際上,許多編譯程序都會忽略register修飾符案狠,因為盡管它完全合法服傍,但它僅僅是暗示而不是命令。
二骂铁、bool類型專題
C++中的布爾類型
- C++在C語言的基本類型系統(tǒng)之上增加了bool
- C++中的bool可取的值只有true和false
- 理論上bool只占用一個字節(jié)伴嗡,如果多個bool變量定義在一起,可能會各占一個bit从铲,這取決于編譯器的實現(xiàn)
- true代表真值瘪校,編譯器內(nèi)部用1來表示
- false代表非真值名段,編譯器內(nèi)部用0來表示
- bool類型只有true(非0)和false(0)兩個值
C++編譯器會在賦值時將非0值轉(zhuǎn)換為true麻惶,0值轉(zhuǎn)換為false
int main(){
int a;
bool b = true;
printf("b = %d,sizeof(b) = %lu \n",b,sizeof(b));
//b = 1,sizeof(b) = 1
a = 4;
b = 5;
printf("a = %d ,b = %d \n",a,b);
//a = 4 ,b = 1
a = -4;
a = b;
printf("a = %d ,b = %d \n",a,b);
//a = 1 ,b = 1
a = 10;
b = a;
printf("a = %d ,b = %d \n",a,b);
//a = 10 ,b = 1
b = 0;
printf("b = %d \n",b);
//b = 0
return 0;
}
/*
打印結(jié)果:
b = 1,sizeof(b) = 1
a = 4 ,b = 1
a = 1 ,b = 1
a = 10 ,b = 1
b = 0
*/
三信夫、三目運算符
int main(){
int a = 10;
int b = 20;
//此處在.c文件中是無法編譯通過的
(a < b ? a : b) = 30;
//返回一個最小數(shù) 并且給最小數(shù)賦值成3
//三目運算符是一個表達式 卡啰,表達式不可能做左值
printf("a = %d ,b = %d \n",a,b);
//a = 30 ,b = 20
return 0;
}
- C語言返回變量的值 C++語言是返回變量本身
- C語言中的三目運算符返回的是變量值警没,不能作為左值使用匈辱。C++中的三目運算符可直接返回變量本身,因此可以出現(xiàn)在程序的任何地方杀迹。
-
注意:三目運算符可能返回的值中如果有一個是常量值亡脸,則不能作為左值使用
(a < b ? 1 : b )= 30;
- C語言如何支持類似C++的特性?
變量的本質(zhì)是內(nèi)存空間的別名树酪,是一個標(biāo)號浅碾。
問題的實質(zhì):
c語言返回的是變量的值,C++語言返回的是變量本身
C/C++中的Const專題
一续语、const基礎(chǔ)知識(用法垂谢、含義、好處)
int main(){
//第一個第二個意思一樣 代表一個常整形數(shù)
const int a = 10;
int const b = 10;
//第三個 c是一個指向常整形數(shù)的指針(所指向的內(nèi)存數(shù)據(jù)不能被修改疮茄,但是本身可以修改)
const int * c ;
int z = 10;
c = &z;
//err: *c = 20;
z = 20;
printf("z = %d \n",z);
//第四個 d 常指針(指針變量不能被修改滥朱,但是它所指向內(nèi)存空間可以被修改)
int * const d = &z;
z = 100;
z = 200;
z = 300;
printf("z = %d \n",z);
int x = 10;
//第五個 e一個指向常整形的常指針(指針和它所指向的內(nèi)存空間,均不能被修改)
const int * const e = &x;
//eer:*e = 400
printf("e = %d \n",e);
return 0;
//初級理解:const是定義常量==》const意味著只讀
}
const好處
合理的利用const:
- 指針做函數(shù)參數(shù)娃豹,可以有效的提高代碼可讀性焚虱,減少bug购裙;
- 清楚的分清參數(shù)的輸入和輸出特性
struct Teacher{
char name[64];
int age;
};
//const修飾指針?biāo)竷?nèi)存空間不能被修改
void printfTeacher(const Teacher *p){
printf("teacher.age: %d \n",p->age);
//err:p->age = 100;
}
//const修飾指針 p變量不能被修改
void printfTeacher2( Teacher * const p){
Teacher t = {"xiaohong",18};
//err:p = &t;
//p = NULL;
}
//const修飾指針 p變量不能被修改
void printfTeacher3(const Teacher * const p){
//Teacher t = {"xiaohong",18};
//err:p = &t;
//p = NULL;
printf("p:age:%d \n", p->age);
}
int main(){
Teacher t = {"liming",10};
printfTeacher3(&t);
return 0;
}
二懂版、C中的“冒牌貨”
int main(){
const int a = 10;
int *p = (int*)&a;
*p = 12;
printf("a===>%d\n", a);
printf("*p===>%d\n", *p);
//*p是10 。躏率。薇芝。。說明p和&a綁定的很死嚷缭。。荐开。
//*p是11.百侧。佣渴。观话。灵迫。說明*p所指的內(nèi)存空間和&a,不一樣瀑粥。狞换。修噪。
printf("&a: %d \n", &a);
printf("p: %d \n", p);
return 0;
}
/*結(jié)果
a===>10
*p===>12
&a: 1435678472
p: 1435678472
*/
解釋:
C++編譯器對const常量的處理
當(dāng)碰見常量聲明時,在符號表中放入常量 =
問題:那有如何解釋取地址
編譯過程中若發(fā)現(xiàn)使用常量則直接以符號表中的值替換
編譯過程中若發(fā)現(xiàn)對const使用了extern或者&操作符脏款,則給對應(yīng)的常量分配存儲空間(兼容C)
const是常量
在C++中撤师,const修飾的是一個真正的常量,而不是C中變量的只讀
int main(){
const int a = 1;
const int b = 2;
int array[a+b] = {0};
int i = 0;
for(i = 0;i < (a + b);i++){
printf("array[%d] = %d \n",i,array[i]);
}
return 0;
}
/*
array[0] = 0
array[1] = 0
array[2] = 0
*/
聯(lián)系const和#define的區(qū)別
對比加深
C++中的const常量類似于宏定義
const int c = 5; ≈ #define c 5
C++中的const常量在與宏定義不同
const常量是由編譯器處理的,提供類型檢查和作用域檢查
宏定義由預(yù)處理器處理淤袜,單純的文本替換
void fun1(){
#define a 10
const int b = 20;
};
void fun2(){
printf("a = %d \n",a);
//err:printf("b = %d \n",b);
};
int main(){
fun1();
fun2();
return 0;
}
//結(jié)果:在func1中定義的a可以在main里面使用闰歪,b不可以
引用專題
一.引用(普通引用)
0.變量名回顧
變量名實質(zhì)上是一段連續(xù)存儲空間的別名库倘,是一個標(biāo)號(門牌號)
程序中通過變量來申請并命名內(nèi)存空間
通過變量的名字可以使用存儲空間
1.引用是C++的概念杆勇,屬于C++編譯器對C的擴展
問題:C中可以編譯通過嗎蚜退?
.c
int main(){
int a = 10;
int * const b = &a;
*b = 11;
return 0;
}
.cpp
int main()
{
int a = 0;
int &b = a;
b = 11;
return 0;
}
結(jié)論:請不要用C的語法考慮 b=11
2.引用概念
- 在C++中新增加了引用的概念
- 引用可以看作一個已定義變量的別名
- 引用的語法:Type& name = var;
- 引用做函數(shù)參數(shù)那钻注?(引用作為函數(shù)參數(shù)聲明時不進行初始化)
int main(){
int a = 10;
int &b = a;
b = 11;
cout<<"b-->"<<a<<endl;
printf("a:%d \n",a);
printf("b:%d \n",b);
printf("&a:%d \n",&a);
printf("&b:%d \n",&b);
/*
b-->11
a:11
b:11
&a:1545963256
&b:1545963256
*/
return 0;
}
3. 引用的意義
- 引用作為其它變量的別名而存在,因此在一些場合可以代替指針
- 引用相對于指針來說具有更好的可讀性和實用性
dome1
int swap1(int &a,int &b){
int t = a;
a = b;
b = t;
return 0;
}
int swap2(int *a,int *b){
int temp = *a;
*a = *b;
*b = temp;
return 0;
}
int main(){
int a = 10;
int b = 20;
int c = 30;
int d = 40;
swap1(a,b);
swap2(&c,&d);
printf("a:%d,b%d,c:%d,d:%d",a,b,c,d);
//a:20,b10,c:40,d:30
return 0;
}
dome2
struct Tearcher{
char name[64];
int age;
};
int getTearcher(Tearcher **myp){
Tearcher *p = (Tearcher*)malloc(sizeof(Tearcher));
if(p == NULL){return -1;};
memset(p,0,sizeof(Tearcher));
p->age = 30;
*myp = p;
return 0;
}
//此處奇怪的寫法實際上是:傳遞指針的引用
int getTearcherRe(Tearcher* &myp){
myp = (Tearcher*)malloc(sizeof(Tearcher));
if(myp == NULL){return -1;};
myp->age = 40;
memset(p,0,sizeof(Tearcher));
return 0;
}
int main(){
Tearcher *p = NULL;
//getTearcher(&p); // log:age:30
getTearcherRe(p); //log:age:40
printf("age:%d \n",p->age);
return 0;
}
4.普通引用有自己的空間嗎捆交?
struct Student{
int &a;
int &b;
};
int main(){
printf("sizeof(Student):%lu \n",sizeof(Student));
//sizeof(Student):16 (mac64位機)
return 0;
}
引用是一個有地址,引用是常量:char * const p
答案:有
5.引用的本質(zhì)
- 引用在C++中的內(nèi)部實現(xiàn)是一個常指針
Type& name <---> Type* const name
- C++編譯器在編譯過程中使用常指針作為引用的內(nèi)部實現(xiàn)肉瓦,因此引用所占用的空間大小與指針相同风宁。
- 從使用的角度蛹疯,引用會讓人誤會其只是一個別名,沒有自己的存儲空間列吼。這是C++為了實用性而做出的細節(jié)隱藏
void func(int &a){a = 5;}
void func(int * const a){*a = 5}
6.引用作為返回值時候的對比
//static修飾變量的時候寞钥,變量是一個狀態(tài)變量
int j(){
static int a = 10;
a++;
printf("a:%d \n",a);
return a;
}
int& j1(){
static int a = 10;
a++;
printf("a:%d \n",a);
return a;
}
int* j2(){
static int a = 10;
a++;
printf("a:%d \n",a);
return &a;
}
int main(){
// 1. j() = 100;//該函數(shù)運算結(jié)果是一個數(shù)值蹄溉,沒有內(nèi)存地址柒爵,不能當(dāng)左值
j();
//2 .當(dāng)被調(diào)用的函數(shù)當(dāng)左值的時候棉胀,必須返回一個引用
j1() = 100;
//3.此處相當(dāng)于我們程序員手工的打造 做左值的條件
*(j2()) = 200;
j2();
return 0;
}
int getA(){
int a ;
a = 10;
return a;
}
//基礎(chǔ)類型a返回的時候唁奢,也會有一個副本驮瞧,即返回的不是函數(shù)內(nèi)的a(需要出棧),而是一個副本
int& getB(){
int a;
a = 10;
return a;
}
int* getC(){
int a;
a = 10;
return &a;
}
int main(){
int a1 = 0;
int a2 = 0;
a1 = getA();
a2 = getB();
//這里是沒有辦法用引用接到 函數(shù)返回值返回來的內(nèi)存地址的
int &a3 = getB();
printf("a1:%d \n",a1);
printf("a2:%d \n",a2);
printf("a3:%d \n",a3);
// a1:10
// a2:10
// a3:0
return 0;
}
- 請仔細對比間接賦值成立的三個條件
- 定義兩個變量 (一個實參一個形參)
- 建立關(guān)聯(lián) 實參取地址傳給形參
- *p形參去間接的修改實參的值
6.C++引用注意
??當(dāng)函數(shù)返回值為引用時狂魔,若返回棧變量最楷,不能成為其它引用的初始值籽孙,不能作為左值使用
??若返回靜態(tài)變量或全局變量犯建,可以成為其他引用的初始值,即可作為右值使用玻熙,也可作為左值使用
??C++鏈?zhǔn)骄幊讨朽滤妫?jīng)常用到引用肌毅,運算符重載專題
7.結(jié)論
引用在實現(xiàn)上悬而,只不過是把:間接賦值成立的三個條件的后兩步和二為一笨奠。
當(dāng)實參傳給形參引用的時候,只不過是c++編譯器幫我們程序員手工取了一個實參地址蔚袍,傳給了形參引用(常量指針)
二.常引用
const引用為難點啤咽,使用的時候應(yīng)該特別注意宇整。
1.const引用(使用變量初始化const引用)
在C++中可以聲明const引用const Type& name = var;
const引用讓變量擁有只讀屬性
int main(){
int a = 10;
const int &b = a;
// b = 11; //err:不允許隨意修改常量引用
int *p = (int *)&b;
*p = 11;
cout<<"b--->"<<a<<endl;
printf("a:%d\n", a);
printf("b:%d\n", b);
printf("&a:%d\n", &a);
printf("&b:%d\n", &b);
/*
b--->11
a:11
b:11
&a:1345795832
&b:1345795832
*/
return 0;
}
2.const引用(使用字面量常量初始化const引用)
分兩種情況:
1臂拓、用變量對const引用初始化,const引用分配內(nèi)存空間了嗎童番?//不分配
2威鹿、用常量對const引用初始化,const引用分配內(nèi)存空間了嗎科雳?//分配
int main(){
const int &a = 10;
// int &b = 10;//err: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'
int *p = (int*)&a;
*p = 12;
printf("a:%d \n",a);
return 0;
}
- 當(dāng)使用常量(字面量)對const引用進行初始化時糟秘,C++編譯器會為常量值分配空間尿赚,并將引用名作為這段空間的別名
- 使用常量對const引用初始化后將生成一個只讀變量
3.結(jié)論const引用
const& 相當(dāng)于 const int * const e
*普通引用 相當(dāng)于 int const e1
函數(shù)專題
一.函數(shù)基礎(chǔ)知識
1.內(nèi)聯(lián)函數(shù)
- inline關(guān)鍵字必須和函數(shù)實現(xiàn)放在一塊
- inline是一個請求,告訴編譯進行內(nèi)聯(lián)編譯
inline void printA(){
printf("test");
}
int main(){
printA();
/*
實際上是講printA函數(shù)大括號的內(nèi)容直接拷貝過來
{
printf("test");
}
*/
return 0;
}
帶參數(shù)的宏和普通函數(shù)區(qū)別
#define MYFUNC(a,b) ((a) < (b) ? (a) : (b))
inline int myFunc(int a,int b){
return a < b ? a : b;
}
int main(){
int a = 1;
int b = 3;
//不要讓頭疼兄弟++i,i++來做函數(shù)參數(shù)
int c = myFunc(++a,b); //c: 3
// int c = MYFUNC(++a,b); // ((++a) < (b) ? (++a) : (b))
printf("a = %d\n", a); //2 //3
printf("b = %d\n", b); //3 //3
printf("c = %d\n", c); //2 //3
//要注意,宏是整個內(nèi)容的替換须教,所以++a會被執(zhí)行兩次
return 0;
}
2.函數(shù)的擴展
- 函數(shù)參數(shù)位置可以加上等號配置默認參數(shù)轻腺,如果不傳遞參數(shù)的時候則按照函數(shù)參數(shù)默認參數(shù)傳遞
- 在默認參數(shù)規(guī)則 约计,如果默認參數(shù)出現(xiàn),那么右邊的都必須有默認參數(shù)
- 有占位參數(shù)的時候尉桩,在函數(shù)調(diào)用的時候需要傳入?yún)?shù)
- 占位參數(shù)如果有默認值的時候蜘犁,可以選擇傳遞或者不傳遞參數(shù)
//默認參數(shù)
void printAB(int x = 3 ){
printf("x = %d \n",x);
}
//在默認參數(shù)規(guī)則 ,如果默認參數(shù)出現(xiàn)导披,那么右邊的都必須有默認參數(shù)
void printABC(int x,int y,int a = 1,int b = 2,int c = 3){
printf("x = %d \n",x);
}
//占位參數(shù)
int func (int x, int y, int )
{
return x + y;
}
int func2(int a, int b, int = 0)
{
return a + b;
}
int main(){
printAB(10);
printAB();
func(1,2,3);
//func(1,2);//err:必須要有三個參數(shù)
//-----------
//如果默認參數(shù)和占位參數(shù)在一起鹰晨,都能調(diào)用起來
func2(1,2);
func2(1,2,3);
return 0;
}
3.函數(shù)重載
函數(shù)重載:函數(shù)名稱一樣漠趁,但是參數(shù)數(shù)量不一樣闯传,類型不一樣,參數(shù)的順序也不一樣
void myprint(int a)
{
printf("a:%d \n", a);
}
void myprint(int a, char *p)
{
printf("a:%d \n", a);
}
void myprint(char *p, int a)
{
printf("a:%d \n", a);
}
void myprint(double a)
{
printf("a:%d \n", a);
}
int main(){
myprint(1);
myprint(2,"3");
myprint("4",5);
return 0;
}
4.重載中存在的二義性錯誤
int func(int a,int b ,int c = 0){
printf("a:%d ", a);
return 0;
}
int func(int a,int b ){
printf("a:%d ", a);
return 0;
}
int main(){
int c = 0;
//c = func(1,2);//call to 'func' is ambiguous 調(diào)用函數(shù)不能存在二義性報錯
c = func(1,2,3);
return 0;
}
5.函數(shù)重載遇上函數(shù)指針
首先復(fù)習(xí)一下如何定義一個類型妹窖,主要是通過:typedef type name
;
//定義一個數(shù)組類型
typedef int MYTYPEArray[10];
MYTYPEArray a;// int a[10];
//定義一個數(shù)組類型指針類型
typedef int (*MyArrayP)[10];
MyArrayP myarrayp = NULL;
//int (*myp)[10];//讓編譯器分配4個字節(jié)的內(nèi)存,該變量為指針蜓萄,指向一個數(shù)組
//定義一個類型嫉沽,這個類型是函數(shù)類型
typedef int(*PFUNC)(int a);
typedef int(*PFUNC2)(const char * p);
以上即是定義一個類型的方法 绸硕,也包括了定義函數(shù)類型玻佩,那么我們應(yīng)該如何使用它呢?
int func(int x){
return x;
}
int func(int x,int y){
return x+y;
}
int func(const char* p){
printf("char:%s",p);
return strlen(p);
}
//定義一個類型咬崔,這個類型是函數(shù)類型
typedef int(*PFUNC)(int a);
typedef int(*PFUNC2)(const char * p);
int main(){
int c = 0;
{
PFUNC p = func;
c = p(1);
}
printf("c:%d \n",c);
{
PFUNC2 myp2 = func;
myp2("abcde");
}
return 0;
}
/*打印結(jié)果
c:1
char:abcde
*/
此處可以直觀的看出我們使用函數(shù)指針指向某個函數(shù)的時候,也用指針來調(diào)用函數(shù)兜蠕。
C++類專題
最基本的類的定義
其實C++中定義一個類狡耻,和其他面向?qū)ο笳Z言也特別相似,學(xué)習(xí)起來也特別快郊霎,關(guān)鍵字是:class + 類名{};
书劝。類可以是空類。引入代碼讓大家更好的理解:
類是把屬性和方法封裝
我們抽象了一個類骡苞,用類去定義對象。類是一個數(shù)據(jù)類型躲株,類是抽象的霜定。對象是一個具體的變量。占用內(nèi)存空間黍图。類做函數(shù)參數(shù)的時候剖张,類封裝了屬性和方法搔弄,在被調(diào)用函數(shù)里面丰滑, 不但可以使用屬性,而且可以使用方法(成員函數(shù))擎宝;
面向過程編程加工的是:函數(shù)
面向?qū)ο缶幊碳庸さ氖牵侯?/p>
基本類的實例
class Cube{
public:
int getA(){
return m_a;
}
int getB(){
return m_b;
}
int getC(){
return m_c;
}
void setABC(int a = 0,int b = 0,int c = 0){
m_a = a;
m_b = b;
m_c = c;
}
void setA(int a)
{
m_a = a;
}
void setB(int b)
{
m_b = b;
}
void setC(int c)
{
m_c = c;
}
public:
int getV(){
m_v = m_a * m_b * m_c;
return m_v;
}
int getS(){
m_s = 2*(m_a*m_b + m_b*m_c + m_a*m_c);
return m_s;
}
private:
int m_a;
int m_b;
int m_c;
int m_v;
int m_s;
};
int main(){
Cube c1;
c1.setA(3);
c1.setB(3);
c1.setC(3);
cout<<"c1的面積是:"<<c1.getV()<<endl;
cout<<"c1的周長是:"<<c1.getS()<<endl;
// c1的面積是:27
// c1的周長是:54
Cube c2;
c2.setABC(5,5,5);
cout<<"c2的面積是:"<<c2.getV()<<endl;
cout<<"c2的周長是:"<<c2.getS()<<endl;
// c2的面積是:125
// c2的周長是:150
return 0;
}
訪問權(quán)限
public/protected/private是訪問權(quán)限控制顾彰,有接觸過的同學(xué)應(yīng)該比較清楚极阅,一個類可以有成員變量,成員屬性組
成涨享。
類的訪問控制筋搏,三個關(guān)鍵字
- public 成員變量和成員函數(shù)可以在類的內(nèi)部和外界訪問和調(diào)用
- protected 成員變量和成員函數(shù)可以在子類的內(nèi)部和外界訪問和調(diào)用
- private 成員變量和成員函數(shù)只能在類的內(nèi)部被訪問和調(diào)用
類的內(nèi)存四區(qū)理解
另外,我們可以用sizeof
打印一下Cube的大小
int main(){
printf("class:siezof(cube)->%lu",sizeof(Cube));
//class:siezof(cube)->20
return 0;
}
可以發(fā)現(xiàn)剛剛那個Cube類的大小是20厕隧,五個成員屬性奔脐,一個int 是4個字節(jié),合計20。這里可以看出來,其實函數(shù)并沒有和成員變量放在一起,而是放在了代碼區(qū)。
然后,我后面在成員遍歷里面添加了一個char* p
的屬性者疤。那么,大家會覺得這次的sizeof會等于多少呢?
class Cube{
......
private:
int m_a;
int m_b;
int m_c;
int m_v;
int m_s;
char * p;
};
int main(){
printf("class:siezof(cube)->%lu",sizeof(int));
//class:siezof(cube)->?
return 0;
}
實際上這個時候打印的是32仗岸,同學(xué)們可以試一下盗痒∧裾可是指針在32系統(tǒng)下不是4個字節(jié)涉茧,64的系統(tǒng)也只是8個字節(jié)钳垮,為何會出現(xiàn) 20 + 8 = 32
呢短荐?后來我再里面添加了一個int d
屬性糠排,打印的依然是32落追。
我查了一下資料疗垛,大致是說,計算機為了內(nèi)存的連續(xù)可讀性,有時候?qū)幵缚罩?個字節(jié)不用,也要把它空出來提高效率和內(nèi)存連續(xù)性嚎朽。所以在我們認為應(yīng)該打印28的時候陷寝,實際上系統(tǒng)給我們的內(nèi)存優(yōu)化,直接占了8位咖耘。
類的真正形態(tài)
- 在用struct定義類時蹈垢,所有成員的默認屬性為public
- Union 默認所有的屬性為public 而且不可修改张足。
- 在用class定義類時蛀恩,所有成員的默認屬性為private
C++中的構(gòu)造和析構(gòu)專題講座
構(gòu)造和析構(gòu)基礎(chǔ)
在函數(shù)內(nèi)聲明了某個類的時候驹尼,會自動調(diào)用該類的空參數(shù)構(gòu)造函數(shù)讲逛,該參數(shù)沒有顯示寫明返回值,在函數(shù)出棧以后预愤,系統(tǒng)會自動調(diào)用該類的析構(gòu)函數(shù),我們可以在析構(gòu)函數(shù)里面釋放內(nèi)存檩赢。
class Test{
public:
//無參數(shù)構(gòu)造函數(shù)
Test(){
p = (char*)malloc(100);
strcpy(p,"abc");
cout<<"我是構(gòu)造函數(shù)乒融,自動被調(diào)用了"<<endl;
}
~Test(){
cout<<"我是析構(gòu)函數(shù)次绘,自動被調(diào)用了"<<endl;
if(p != NULL){
free(p);
}
}
private:
int a;
char * p ;
};
//演示類的生命周期
void ObjectPlay(){
Test t1,t2;
cout<<"展示t1.t2的生命周期"<<endl;
}
int main(){
ObjectPlay();
return 0;
/*
我是構(gòu)造函數(shù)轩缤,自動被調(diào)用了
我是構(gòu)造函數(shù)治力,自動被調(diào)用了
展示t1.t2的生命周期
我是析構(gòu)函數(shù)涤伐,自動被調(diào)用了
我是析構(gòu)函數(shù)掌动,自動被調(diào)用了
*/
}
類的構(gòu)造函數(shù)三種形式
以下是多參數(shù)和單參數(shù)的對比:
class Test{
public:
//無參數(shù)構(gòu)造函數(shù)
Test(){}
//帶參數(shù)的構(gòu)造函數(shù)
Test(int mya){
a = mya;
}
//復(fù)制構(gòu)造函數(shù)
Test(const Test & obj){
;
}
private:
int a;
int b;
};
int main(){
//三種實例化方法
Test t1(1);//c++默認調(diào)用有參構(gòu)造函數(shù) 自動調(diào)用
Test t2 = 2;//c++默認調(diào)用有參構(gòu)造函數(shù) 自動調(diào)用
Test t3 = Test(3);//我們程序員手動調(diào)用構(gòu)造函數(shù)
return 0;
}
class Test
{
public:
Test() //無參構(gòu)造函數(shù) 默認構(gòu)造函數(shù)
{
p = (char *)malloc(100);
strcpy(p, "11111");
cout<<"我是構(gòu)造函數(shù)妖碉,自動被調(diào)用了"<<endl;
}
Test(int _a=0, int _b=0) //無參構(gòu)造函數(shù) 默認構(gòu)造函數(shù)
{
p = (char *)malloc(100);
strcpy(p, "11111");
a = _a;
b = _b;
cout<<"我是構(gòu)造函數(shù),自動被調(diào)用了"<<endl;
}
~Test()
{
cout<<"我是析構(gòu)函數(shù)嚎尤,自動被調(diào)用了"<<endl;
if (p != NULL)
{
free(p);
}
}
protected:
private:
int a;
int b;
char *p ;
};
//單獨搭建一個舞臺
void ObjPlay()
{
Test t1(1, 2);
Test t2 = (1, 2);
Test t3 = Test(3, 4);
cout<<"展示類的生命周期"<<endl;
}
int main(){
ObjPlay();
/*
我是構(gòu)造函數(shù)揖曾,自動被調(diào)用了
我是構(gòu)造函數(shù),自動被調(diào)用了
我是構(gòu)造函數(shù)亥啦,自動被調(diào)用了
展示類的生命周期
我是析構(gòu)函數(shù)炭剪,自動被調(diào)用了
我是析構(gòu)函數(shù),自動被調(diào)用了
我是析構(gòu)函數(shù)翔脱,自動被調(diào)用了
*/
return 0;
}
拷貝構(gòu)造函數(shù)的四種情景
一般情況有四種選擇來調(diào)用復(fù)制構(gòu)造函數(shù):
- 情景一:
AA a2 = a1
即用 = 賦值等于符號 - 情景二:
AA a2(a1)
用另外一個類變量作為參數(shù)獲得新的函數(shù)
class AA{
public:
AA(){
cout<<"我是構(gòu)造函數(shù)AA()奴拦,自動被調(diào)用了"<<endl;
}
AA(int _a){
a = _a;
cout<<"我是構(gòu)造函數(shù)AA(int _a),自動被調(diào)用了"<<endl;
}
AA(const AA &obj2){
a = obj2.a + 10;
cout<<"我也是構(gòu)造函數(shù)届吁,我是通過另外一個對象obj2错妖,來初始化我自己"<<endl;
}
~AA(){
cout<<"我是析構(gòu)函數(shù),自動被調(diào)用了"<<endl;
}
void getA(){
printf("a : %d \n",a);
}
private:
int a;
};
void ObjPlay01(){
//定義一個變量
AA a1(10);
//賦值構(gòu)造函數(shù)的第一個應(yīng)用場景
//我用對象1 初始化 對象2
AA a2 = a1; //定義變量并初始化
a1.getA();
a2.getA();
printf("__________ \n");
a2 = a1;//用a1來=號給a2 編譯器給我們提供的淺copy
a1.getA();
a2.getA();
/*
我是構(gòu)造函數(shù)AA(int _a)瓷产,自動被調(diào)用了
我也是構(gòu)造函數(shù)站玄,我是通過另外一個對象obj2,來初始化我自己
a : 10
a : 20
__________
a : 10
a : 10
我是析構(gòu)函數(shù)濒旦,自動被調(diào)用了
我是析構(gòu)函數(shù),自動被調(diào)用了
*/
}
//單獨搭建一個舞臺
void ObjPlay02()
{
AA a1(10); //變量定義
//賦值構(gòu)造函數(shù)的第一個應(yīng)用場景
//我用對象1 初始化 對象2
AA a2(a1); //定義變量并初始化
//a2 = a1; //用a1來=號給a2 編譯器給我們提供的淺copy
a2.getA();
/*
我是構(gòu)造函數(shù)AA(int _a)再登,自動被調(diào)用了
我也是構(gòu)造函數(shù)尔邓,我是通過另外一個對象obj2卿操,來初始化我自己
a : 20
我是析構(gòu)函數(shù)顽腾,自動被調(diào)用了
我是析構(gòu)函數(shù)熊户,自動被調(diào)用了
*/
}
int main(){
//ObjPlay01();
ObjPlay02();
return 0;
}
- 情景三:
Location(const Location & p)
該函數(shù)會在被當(dāng)做實參的時候被調(diào)用读第,拷貝一份到執(zhí)行函數(shù)里面肥照,不影響原函數(shù)
注意:如果你寫了copy構(gòu)造函數(shù)嗜诀,那么編譯器不會在提供無參構(gòu)造函數(shù)汁掠;如果你寫了有參或者無參構(gòu)造函數(shù)唠摹,那么編譯器也不會提供無參構(gòu)造函數(shù)
class Location{
public:
Location(){}
Location(int _x ,int _y){
x = _x;
y = _x;
cout<<"構(gòu)造函數(shù)"<<endl;
}
Location(const Location & p){
x = p.x;
y = p.y;
cout<<"復(fù)制構(gòu)造函數(shù)"<<endl;
}
int getX(){
return x;
}
int getY(){
return y;
}
private:
int x;
int y;
};
void f(Location p){
cout << "Funtion:" << p.getX() << "," << p.getY() << endl ;
}
void objPlaying(){
Location A(1,2);
cout << "------上面執(zhí)行的是構(gòu)造函數(shù),下面是復(fù)制構(gòu)造函數(shù)------" << endl ;
f(A);
}
int main(){
objPlaying();
/*
構(gòu)造函數(shù)
------上面執(zhí)行的是構(gòu)造函數(shù)炎疆,下面是復(fù)制構(gòu)造函數(shù)------
復(fù)制構(gòu)造函數(shù)
Funtion:1,1
*/
return 0;
}
- 情景四:函數(shù)返回值為類卡骂,這個時候函數(shù)銷毀后,類是否被析構(gòu)形入,分兩種情況全跨。
類結(jié)構(gòu)和原來一致,不再復(fù)制黏貼亿遂,以下是第四種情況的代碼以及分析
Location g(){
Location A(1,2);
return A;
}
void objPlaying2(){
//第一種情況:先聲明浓若,再用函數(shù)接過來
/*
Location A;
A = g();
cout << "------這個時候已經(jīng)執(zhí)行了一次析構(gòu)函數(shù),將函數(shù)內(nèi)的形參析構(gòu)------" << endl ;
打印結(jié)果:
構(gòu)造函數(shù)
析構(gòu)函數(shù)
------這個時候已經(jīng)執(zhí)行了一次析構(gòu)函數(shù)蛇数,將函數(shù)內(nèi)的形參析構(gòu)------
析構(gòu)函數(shù)
*/
//第二種情況挪钓,直接將返回值接過來
//如果返回的匿名對象,來初始化另外一個同類型的類對象耳舅,那么匿名對象會直接轉(zhuǎn)成新的對象碌上。
//匿名對象的去和留,關(guān)鍵看挽放,返回時如何接過來绍赛。
Location B = g();
cout << "------此時還未執(zhí)行析構(gòu)函數(shù),會將函數(shù)自己接過來------" << endl ;
/*
構(gòu)造函數(shù)
------此時還未執(zhí)行析構(gòu)函數(shù)辑畦,會將函數(shù)自己接過來------
析構(gòu)函數(shù)
*/
}
int main(){
objPlaying2();
return 0;
}
多個對象初始化問題的研究
??此處主要是分析了吗蚌,如果一個對象里面含有另外一個對象,那么另外一個對象要查看是否使用默認構(gòu)造函數(shù)纯出,如果沒有使用默認的無參構(gòu)造函數(shù)的時候蚯妇,就要在類對象的初始化列表里面進行對另外一個對象的初始化。如:B():A(10),A(20)
暂筝。
??成員變量的初始化順序與聲明的順序(屬性列表順序)相關(guān)箩言,與在初始化列表中的順序無關(guān)
使用初始化列表出現(xiàn)原因:
如果我們有一個類成員,它本身是一個類或者是一個結(jié)構(gòu)焕襟,而且這個成員它只有一個帶參數(shù)的構(gòu)造函數(shù)陨收,而沒有默認構(gòu)造函數(shù),這時要對這個類成員進行初始化鸵赖,就必須調(diào)用這個類成員的帶參數(shù)的構(gòu)造函數(shù)务漩,如果沒有初始化列表,那么他將無法完成第一步它褪,就會報錯饵骨。
類成員中若有const修飾,必須在對象初始化的時候茫打,給const int m 賦值.當(dāng)類成員中含有一個const對象時居触,或者是一個引用時妖混,他們也必須要通過成員初始化列表進行初始化,因為這兩種對象要在聲明后馬上初始化轮洋,而在構(gòu)造函數(shù)中制市,做的是對他們的賦值,這樣是不被允許的砖瞧。
class A{
public:
A(int _a){
a = _a;
}
void printA(){
printf("A a:%d \n",a);
}
private:
int a;
};
//構(gòu)造函數(shù)的初始化列表產(chǎn)生原因
class B{
public:
B():mya(12),mya2(13){
;
}
B(int x,int y):mya(y),mya2(100){
b = x;
}
private:
int b;
A mya;
A mya2;
};
int main(){
A a1(10);
B b1(10,20);
return 0;
}
構(gòu)造和析構(gòu)的調(diào)用順序(成員屬性里面包含了另外一個類的時候)
class ABCD
{
public:
ABCD(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
printf("ABCD() construct, a:%d,b:%d,c:%d \n", this->a, this->b, this->c);
}
~ABCD()
{
printf("~ABCD() construct,a:%d,b:%d,c:%d \n", this->a, this->b, this->c);
}
int getA()
{
return this->a;
}
protected:
private:
int a;
int b;
int c;
};
class MyE
{
public:
MyE():abcd1(1,2,3),abcd2(4,5,6),m(100)
{
cout<<"MyE()"<<endl;
}
~MyE()
{
cout<<"~MyE()"<<endl;
}
MyE(const MyE & obj):abcd1(7,8,9),abcd2(10,11,12),m(100)
{
printf("MyE(const MyE & obj)\n");
}
protected:
//private:
public:
ABCD abcd1; //c++編譯器不知道如何構(gòu)造abc1
ABCD abcd2;
const int m;
};
int doThing(MyE mye){
printf("doThing() mye1.abc1.a:%d \n", mye.abcd1.getA());
return 0;
}
int run(){
MyE mye;
doThing(mye);
}
int main(){
run();
return 0;
}
調(diào)試代碼路上息堂,下面我們對打印結(jié)果進行分析:
ABCD() construct, a:1,b:2,c:3 成員列表中的第一個屬性初始化
ABCD() construct, a:4,b:5,c:6 成員列表中的第二個屬性初始化
MyE() 調(diào)用類本身的構(gòu)造函數(shù)
ABCD() construct, a:7,b:8,c:9 預(yù)備調(diào)用doThing函數(shù),類變量作為形參傳遞块促,調(diào)用復(fù)制構(gòu)造函數(shù)前荣堰,第一個屬性初始化
ABCD() construct, a:10,b:11,c:12 預(yù)備調(diào)用doThing函數(shù),類變量作為形參傳遞竭翠,調(diào)用復(fù)制構(gòu)造函數(shù)前振坚,第二個屬性初始化
MyE(const MyE & obj) 正式調(diào)用復(fù)制構(gòu)造
doThing() mye1.abc1.a:7 正式調(diào)用doThing函數(shù)
~MyE() doThing函數(shù)結(jié)束,銷毀構(gòu)造函數(shù)的MyE
~ABCD() construct,a:10,b:11,c:12 doThing函數(shù)結(jié)束斋扰,銷毀構(gòu)造函數(shù)的MyE,銷毀第二個屬性
~ABCD() construct,a:7,b:8,c:9 doThing函數(shù)結(jié)束渡八,銷毀構(gòu)造函數(shù)的MyE,銷毀第一個屬性
~MyE() run函數(shù)結(jié)束,
~ABCD() construct,a:4,b:5,c:6 run函數(shù)結(jié)束,銷毀構(gòu)造函數(shù)的MyE,銷毀第二個屬性
~ABCD() construct,a:1,b:2,c:3 run函數(shù)結(jié)束,銷毀構(gòu)造函數(shù)的MyE,銷毀第一個屬性
New Delete基礎(chǔ)專題
new delete 操作符號是c++的關(guān)鍵字,相當(dāng)于C語言中的malloc和free函數(shù)
new 在堆上分配內(nèi)存;delete 分配基礎(chǔ)類型 传货、分配數(shù)組類型屎鳍、分配對象
基礎(chǔ)類型,數(shù)組问裕,類的使用方法
基礎(chǔ)類型的使用:
int main(){
int *p = (int*)malloc(sizeof(int));
free(p);
int *p2 = new int;//和上面的函數(shù)相等
*p2 = 101;
printf("*p: %d",*p2);
delete p2;
//分配內(nèi)存的同時逮壁,初始化
int *p3 = new int(100);
delete p3;
return 0;
}
new數(shù)組類型的使用:
//new 數(shù)組
int main(){
int *p = (int* )malloc(10*sizeof(int));//int a[10];
p[0] = 1;
free(p);
int *p2 = new int[10];
p2[0] = 1;
p2[1] = 2;
delete [] p2;
return 0;
}
類的使用:
class Test{
public:
Test(int _a ,int _b){
a = _a;
b = _b;
cout<<"構(gòu)造函數(shù),我被調(diào)用了"<<endl;
}
~Test(){
cout<<"析構(gòu)函數(shù)粮宛,我被調(diào)用了"<<endl;
}
int getA(){
return a;
}
private:
int a;
int b;
};
int getTestObj(Test **myp){
Test*p = new Test(1,2);
*myp = p;
return 0;
}
int main(){
// 分配內(nèi)存的兩種方式
// 第一種:在棧區(qū)分配內(nèi)存
// Test t1(1,2);
//第二種:在堆區(qū)分配內(nèi)存:使用new關(guān)鍵字會返回一個內(nèi)存的首地址
//new 操作符會自動調(diào)用該類的構(gòu)造函數(shù)
//delete 自動調(diào)用析構(gòu)函數(shù)
// 相當(dāng)于我們程序員可以手工控制類的對象的生命周期
Test *p = new Test(1,2);
cout<<p->getA()<<endl;
delete p;
Test*p2 = NULL;
cout<<"------"<<endl;
getTestObj(&p2);
delete p2;
{
//malloc不會調(diào)用這個類的構(gòu)造函數(shù)
Test*p3 = (Test *)malloc(sizeof(Test));
delete p3;
}
return 0;
}
static關(guān)鍵字專題
static關(guān)鍵字其實也算是比較常見的窥淆,在其他語言中,我們把定義了static的函數(shù)稱為類函數(shù)巍杈,static的變量稱為類成員變量忧饭。static所標(biāo)識的函數(shù)和成員變量和類是否實例化無關(guān)。我們最常見的設(shè)計模式單例模式(保持實例只有一份)筷畦,工廠模式(簡化獲取實例的代碼词裤,類方法調(diào)用獲得實例)都經(jīng)常應(yīng)用該關(guān)鍵字。
class Test{
public:
Test(int _a ,int _b){
a = _a;
b = _b;
cout<<"構(gòu)造函數(shù)鳖宾,我被調(diào)用了"<<endl;
}
~Test(){
cout<<"析構(gòu)函數(shù)亚斋,我被調(diào)用了"<<endl;
}
static void getC(){
c++;
cout<<c<<endl;
}
//在類的靜態(tài)數(shù)據(jù)成員函數(shù)中,是不能調(diào)用具體的對象的變量的屬性
static void getMem(){
// cout<<a<<endl;
}
private:
int a;
int b;
// static修飾的變量攘滩,是屬于類,纸泡,所有的對象都能共享用漂问。
static int c;
};
int Test::c = 10;//err
int main(){
Test t1(1,2);
Test t2(2,3);
t1.getC();
t2.getC();
return 0;
}
cplusplus對象管理模型初探
這部分的內(nèi)容之前有講過赖瞒,主要是三個int變量占的內(nèi)存空間是12 ,但我們在嘗試加多一個static關(guān)鍵和兩個成員函數(shù)以后蚤假,我們的大小依然為12栏饮,由此可以引發(fā)我們的思考--static所標(biāo)記的成員變量和函數(shù)并沒有直接和類對象模型綁定在一起,而是有著自己獨立的區(qū)域磷仰。
C++類對象中的成員變量和成員函數(shù)是分開存儲的
成員變量:
普通成員變量:存儲于對象中袍嬉,與struct變量有相同的內(nèi)存布局和字節(jié)對齊方式
靜態(tài)成員變量:存儲于全局數(shù)據(jù)區(qū)中
成員函數(shù):存儲于代碼段中。
問題出來了:很多對象公用一塊代碼灶平?代碼是如何區(qū)分具體對象的那伺通?
換句話說:int getK() const { return k; },代碼是如何區(qū)分逢享,具體obj1罐监、obj2、obj3對象的k值瞒爬?
C++編譯器對普通成員函數(shù)的內(nèi)部處理
總結(jié)
- C++類對象中的成員變量和成員函數(shù)是分開存儲的弓柱。C語言中的內(nèi)存四區(qū)模型仍然有效!
2.C++中類的普通成員函數(shù)都隱式包含一個指向當(dāng)前對象的this指針侧但。
3.靜態(tài)成員函數(shù)矢空、成員變量屬于類
靜態(tài)成員函數(shù)與普通成員函數(shù)的區(qū)別
靜態(tài)成員函數(shù)不包含指向具體對象的指針
普通成員函數(shù)包含一個指向具體對象的指針
class A{
public:
int i;//4
int j;//4
int k;//4
};//12
class B{
public:
int i;//4
int j;//4
int k;//4
static int m;//4
public:
int getK() const { return k; } //4
void setK(int val) { k = val; } //4
};//12
int main(){
printf("sizeof(A):%lu \n",sizeof(A));
printf("sizeof(B):%lu \n",sizeof(B));
return 0;
}