C++入門基礎(chǔ)

C++入門基礎(chǔ)

namespace專題講座

namespace概念

所謂namespace,是指標(biāo)識符的各種可見范圍。Cpp標(biāo)準(zhǔn)程序庫中的所有標(biāo)識符都被定義于一個名為std的namespace中放坏。

  1. <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春瞬。
  1. 由于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命名空間定義及使用語法

  1. 在Cpp中,名稱(name)可以是符號常量稽揭、變量红伦、宏、函數(shù)淀衣、結(jié)構(gòu)、枚舉召调、類和對象等等膨桥。為了避免,
    在大規(guī)模程序的設(shè)計中唠叛,以及在程序員使用各種各樣的Cpp庫時只嚣,這些標(biāo)識符的命名發(fā)生沖突,
    標(biāo)準(zhǔn)Cpp引入了關(guān)鍵字namespace(命名空間/名字空間/名稱空間/名域)艺沼,可以更好地控制標(biāo)識符的作用域册舞。

  2. 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>).::

  3. C中的命名空間
    在C語言中只有一個全局作用域
    C語言中所有的全局標(biāo)識符共享同一個作用域
    標(biāo)識符之間可能發(fā)生沖突
    Cpp中提出了命名空間的概念
    命名空間將全局作用域分成不同的部分
    不同命名空間中的標(biāo)識符可以同名而不會發(fā)生沖突
    命名空間可以相互嵌套
    全局作用域也叫默認命名空間

    Cpp命名空間的定義:
    namespace name {  …  }
  1. 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)容增強:

  1. register關(guān)鍵字的變化
    register關(guān)鍵字請求“編譯器”將局部變量存儲于寄存器中
    C語言中無法取得register變量地址
    在C++中依然支持register關(guān)鍵字
    C++編譯器有自己的優(yōu)化方式谋减,不使用register也可能做優(yōu)化
    C++中可以取得register變量的地址

  2. C++編譯器發(fā)現(xiàn)程序中需要取register變量的地址時牡彻,register對變量的聲明變得無效。

  3. 早期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;
}
  1. C語言返回變量的值 C++語言是返回變量本身
  2. C語言中的三目運算符返回的是變量值警没,不能作為左值使用匈辱。C++中的三目運算符可直接返回變量本身,因此可以出現(xiàn)在程序的任何地方杀迹。
  3. 注意:三目運算符可能返回的值中如果有一個是常量值亡脸,則不能作為左值使用
    (a < b ? 1 : b )= 30;
  4. 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:

  1. 指針做函數(shù)參數(shù)娃豹,可以有效的提高代碼可讀性焚虱,減少bug购裙;
  2. 清楚的分清參數(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.引用概念
  1. 在C++中新增加了引用的概念
  2. 引用可以看作一個已定義變量的別名
  3. 引用的語法:Type& name = var;
  4. 引用做函數(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ì)
  1. 引用在C++中的內(nèi)部實現(xiàn)是一個常指針
    Type& name <---> Type* const name
  2. C++編譯器在編譯過程中使用常指針作為引用的內(nèi)部實現(xiàn)肉瓦,因此引用所占用的空間大小與指針相同风宁。
  3. 從使用的角度蛹疯,引用會讓人誤會其只是一個別名,沒有自己的存儲空間列吼。這是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;
}

  1. 請仔細對比間接賦值成立的三個條件
    • 定義兩個變量 (一個實參一個形參)
    • 建立關(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)原因:

  1. 如果我們有一個類成員,它本身是一個類或者是一個結(jié)構(gòu)焕襟,而且這個成員它只有一個帶參數(shù)的構(gòu)造函數(shù)陨收,而沒有默認構(gòu)造函數(shù),這時要對這個類成員進行初始化鸵赖,就必須調(diào)用這個類成員的帶參數(shù)的構(gòu)造函數(shù)务漩,如果沒有初始化列表,那么他將無法完成第一步它褪,就會報錯饵骨。

  2. 類成員中若有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ǔ)專題

  1. new delete 操作符號是c++的關(guān)鍵字,相當(dāng)于C語言中的malloc和free函數(shù)

  2. 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é)

  1. 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;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市禀横,隨后出現(xiàn)的幾起案子屁药,更是在濱河造成了極大的恐慌,老刑警劉巖燕侠,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件者祖,死亡現(xiàn)場離奇詭異,居然都是意外死亡绢彤,警方通過查閱死者的電腦和手機七问,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茫舶,“玉大人械巡,你說我怎么就攤上這事∪氖希” “怎么了讥耗?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疹启。 經(jīng)常有香客問我古程,道長,這世上最難降的妖魔是什么喊崖? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任挣磨,我火速辦了婚禮雇逞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘茁裙。我一直安慰自己塘砸,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布晤锥。 她就那樣靜靜地躺著掉蔬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪矾瘾。 梳的紋絲不亂的頭發(fā)上女轿,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音霜威,去河邊找鬼谈喳。 笑死,一個胖子當(dāng)著我的面吹牛戈泼,可吹牛的內(nèi)容都是我干的婿禽。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼大猛,長吁一口氣:“原來是場噩夢啊……” “哼扭倾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起挽绩,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤膛壹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后唉堪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體模聋,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年唠亚,在試婚紗的時候發(fā)現(xiàn)自己被綠了链方。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡灶搜,死狀恐怖祟蚀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情割卖,我是刑警寧澤前酿,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站鹏溯,受9級特大地震影響罢维,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜丙挽,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一言津、第九天 我趴在偏房一處隱蔽的房頂上張望攻人。 院中可真熱鬧,春花似錦悬槽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至猿棉,卻和暖如春磅叛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背萨赁。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工弊琴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杖爽。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓敲董,卻偏偏與公主長得像,于是被迫代替她去往敵國和親慰安。 傳聞我的和親對象是個殘疾皇子腋寨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

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

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,506評論 1 51
  • 第一章 計算機與C++編程簡介 C++程序6個階段編程 ->預(yù)處理->編譯->連接->裝入->執(zhí)行1.程序在編譯器...
    rogertan30閱讀 3,683評論 0 1
  • 重新系統(tǒng)學(xué)習(xí)下C++;但是還是少了好多知識點化焕;socket萄窜;unix;stl撒桨;boost等查刻; C++ 教程 | 菜...
    kakukeme閱讀 19,799評論 0 50
  • 注:這是第三遍讀《C語言深度解剖》,想想好像自從大學(xué)開始就沒讀完過幾本書凤类,其中譚浩強的那本《C語言程序設(shè)計(第四版...
    HavenXie閱讀 1,717評論 1 6
  • 還有最近比較忙穗泵。有點顧不上。如果有我有什么做得不好踱蠢,記得及時跟我說火欧。也發(fā)生了許多意外狀況,相信各位也有茎截。 ...
    澤龍Shawn閱讀 134評論 0 0