6-C++遠征之封裝篇[上]-學習筆記

C++遠征之封裝篇(上)

課程簡介

  • 類(抽象概念),對象(真實具體)
  • 配角: 數據成員和成員函數(構成了精彩而完整的類)
  • 構造函數 & 析構函數(描述了對象的生生死死)
  • 對象複製和對象賦值 (使類的定義充滿藝術)
  • 對象數組和對象指針 (將應用型態發揮到淋漓盡致)
  • this指針(影子,貫穿大戲始終,卻不多嶄露頭角)

你們的思惟模式會從面向過程到面向對象。處理更復雜程序。ios

學完以後,搞一個迷宮的尋路程序。c++

類和對象

  • 類(概念,從對象中抽象出的)和對象(具體的事物)

人類忠實的朋友:程序員

mark

狗有本身的狗狗信息,也有它的技能。一條狗就是一個對象,多條狗爲了便於管理,爲一大羣狗創建起表格。數組

mark

除過表格信息,他們還有共同技能: 叫和跑函數

從上面的狗狗,咱們就能夠抽象出一個狗類。學習

c++類定義

數據成員(屬性)、成員函數(方法)this

思考: 抽象出來的是不是狗的所有信息編碼

結論:目的不一樣抽象出的信息不一樣,咱們只抽象出咱們本身須要的信息。spa

mark

經過電視機上的銘牌,咱們能夠獲得它的屬性信息。經過旋鈕等,咱們能夠操做電視機。3d

封裝: 選擇性暴露(把實現細節封裝起來,只暴露給用戶他們關心的細節)。

mark

這些信息都在類中進行了定義,如何把想暴露的暴露出去,把想隱藏的隱藏起來呢?

訪問限定符: public公共的,protected受保護的,private私有的

mark

內容總結:

  • 什麼是類,什麼是對象
  • 類的定義: 數據成員,成員函數
  • 類的訪問限定符(public private)

C++類對象的定義

對象的實例化

從類中將對象實例化出來,就是計算機根據類這個模板,製造出多個對象的過程。

mark

實例化對象的兩種方式:

  • 從棧中實例化
  • 從堆中實例化

從棧中實例化對象

2-2-StackInstantiatedObject/main.cpp

#include <iostream>
#include <stdlib.h>
using namespace std;

class TV
{
   public:
      char name[20];
      int type;

      void changeVol();
      void power();
};

int main(void)
{
    TV tv;//定義一個對象
    // TV tv[20]; //定義一個對象數組 
    return 0;
}//從棧中實例化對象  會自動回收

注意,務必要在類定義完成以後加上;

從堆中實例化對象.

  • 從堆中實例化的對象。須要咱們手動管理new delete
  • 從棧中申請實例化的不須要手動維護。系統會自動回收。
int main(void)
{
    TV *p = new TV(); //在堆中實例化一個對象
    // TV *q = new TV[20]; // 定義一個對象數組

    delete p;
    // delete []q;

    return 0;
}//從堆中實例化對象

new運算符申請出來的內存就是在堆上的了。

實例化出的對象固然不是一個擺設,咱們要經過訪問對象的各類成員來達成目的。

對象成員的訪問

經過不一樣方式實例化出來的對象,在對象成員,成員函數上的訪問方式也不一樣。

棧實例化出來的對象使用.進行對象成員訪問。

int main(void)
{
    TV tv;//定義一個對象
    tv.type = 0;
    tv.changeVol();
    return 0;
}//從棧中實例化對象  自動回收

堆實例化出來的對象使用->進行對象成員訪問。

int main(void)
{
    TV *p = new TV();
    p -> type = 0;
    p -> changeVol();

    delete p;
    p = NULL;

    return 0;

}//從堆中實例化對象

當堆中實例化的對象是數組時,代碼示例以下:

int main(void)
{
    
    TV *q = new TV[5];
    for (int i = 0; i < 5; ++i)
    {
        p[i] ->type =0;
        p[i] ->changeVol();
    }

    delete []q;
    p = NULL;
    return 0;

}//從堆中實例化對象

代碼演示:

定義一個座標類:包含x,y兩個數據成員。分別打印x和打印y成員函數。

類名最好要能看出類的功能

2-2-CoordinateClassStackHeap/main.cpp

#include <stdlib.h>
#include <iostream>
using namespace std;

class Coordinate {
public:
    int x;
    int y;
    void printx(){
        cout << x << endl;
    }
    void printy() {
        cout << y << endl;
    }
};
int main(void)
{
    Coordinate coor;
    coor.x = 10;
    coor.y = 20;
    coor.printx();
    coor.printy();

    Coordinate *p = new Coordinate();
    if (NULL == p) {
        //failed
        return 0;
    }
    p->x = 100;
    p->y = 200;
    p->printx();
    p->printy();

    delete p;
    p = NULL;

    system("pause");
    return 0;
}

mark

申請內存失敗狀況處理,釋放內存,指針置空。

c++初始化String

使用頻繁,操做繁瑣的數據:

mark

這些都是用的頻繁但操做簡單的數據類型。

  • char數組:

字符串數組操做函數: (strlen | strcat | strcpy | strcmp | strncpy |strncmp | strstr)

  • 字符串類型string類型

string類型

3-1-stringDemo/main.cpp

#include <stdlib.h>
#include <iostream>
#include <string>
using namespace std;

int main(void)
{
    string name = "mtianyan";
    string hobby("football");
    cout << name << endl;
    cout<<hobby << endl;
    system("pause");
    return 0;
}

mark

注意引入string.h,其命名空間也是std

初始化string對象的多個方式

string s1;        //s1爲空串
string s2("ABC"); //用字符串字面值初始化s2;
string s3(s2);    //將s3初始化爲s2的一個副本。
string s4(n,'c')  //將s4初始化爲字符‘c’的n個副本。3個ccc s4='ccc'

string的經常使用操做

s.empty() //若s爲空串,返回true,不然返回false;
s.size()  //返回s中字符的個數
s[n]      //返回s中位置爲n的字符,位置從0開始
s1 + s2   //將兩個字符串鏈接成新串,返回新生成的串
s1 = s2   //把s1的內容替換爲s2的副本;
v1 == v2  //斷定相等,相等返回true,不然返回false
v1 != v2  //斷定不等,不等返回true,不然返回false

經過點的方式,說明s是一個對象

s1+s2 的思惟陷阱

非法字符串link

純字符串鏈接爲非法操做。只有純字符串和string,以及string與string是合法的。

代碼演示

題目

由於要斷定輸入是否是爲空。因此不能簡單的使用cin而應該使用getline

3-1-NameStringDemo/main.cpp

#include <stdlib.h>
#include <iostream>
#include <string>
using namespace std;


int main(void)
{

    string name;
    cout << "please input your name:";
    getline(cin, name);
    if (name.empty()){
        cout << "input is null" << endl;
        system("pause");
        return 0;
    }
    if (name == "mtianyan")
    {
        cout << "you are a admin" << endl;

    }
    cout << "hello ," + name << endl;
    cout << "your name length is :" << name.size() << endl;
    cout << "your name frist letter is :" << name[0] << endl;

    system("pause");
    return 0;
}

c++中經過getline獲取外界控制檯輸入(當包含可能輸入爲空狀況)。通常狀況仍是cin好了。

管理員:

mark

名字爲空:

mark

其餘普通名字

mark

單元鞏固

定義一個Student類,包含名字和年齡兩個數據成員,實例化一個Student對象,並打印出其成兩個數據成員

3-2-StudentClassString/main.cpp

#include <stdlib.h>
#include <iostream>
#include <string>
using namespace std;

/**
* 定義類:Student
* 數據成員:名字、年齡
*/
class Student
{
public:
    // 定義數據成員名字 m_strName 和年齡 m_iAge
    string m_strName;
    int m_iAge;
};

int main()
{
    // 實例化一個Student對象stu
    Student stu;
    // 設置對象的數據成員
    stu.m_strName = "mtianyan";
    stu.m_iAge = 21;

    // 經過cout打印stu對象的數據成員
    cout << stu.m_strName << " " << stu.m_iAge << endl;
    system("pause");
    return 0;
}

mark

C++屬性封裝之初識封裝

數據的封裝

mark

以前咱們的用法是如上圖所示。可是這樣是不符合面向對象的指導思想。

面向對象的基本思想:(以對象爲中心,以誰作什麼表達程序邏輯)

  • 將全部問題轉化爲誰作什麼,對象在程序中的全部行爲,即調用成員函數解決問題。
  • 經過函數封裝數據成員 set & get 方法。

mark

封裝的好處: 符合面向對象的思想,在set中對於參數條件進行限制(防止數據非法,如年齡1000)

mark

數據只讀不寫(只讀屬性):只寫get方法,不寫set方法。

mark

輪子個數應該只能讀,而不能被外界改變設置。

C代碼屬性封裝代碼演示

定義一個student類,含有以下信息

  • 姓名:name
  • 性別 :gender
  • 學分(只讀) :score
  • 學習:study方法

4-2-StudentEncapsulation/main.cpp

#include <stdlib.h>
#include <iostream>
#include <string>

using namespace std;
class Student {
public:

    string getName() const 
    {
        return m_strName; 
    }

    void setName(string _name) 
    { 
        m_strName = _name; 
    }

    string getGender()
    {
        return m_strGender;
    }

    void setGender(string val)
    {
        m_strGender = val;
    }

    int getScore() {
        return m_iScore;
    }

    void study(int _score) {
        m_iScore += _score; 
    }

    void initScore() {
        m_iScore = 0;
    }
private:
    string m_strName;
    string m_strGender;
    int m_iScore;

};

int main(void)
{
    Student stu;
    stu.initScore();
    stu.setName("天涯明月笙");
    stu.setGender("男");
    stu.study(5);
    stu.study(3);

    cout << stu.getName() << " " << stu.getGender() << " " << stu.getScore() << endl;
    
    system("pause");
    return 0;
}

mark

不賦初值:

mark

注意,賦初值。後面會學到構造函數,它是專用來初始化的。

單元鞏固

定義一個Student類,包含名字一個數據成員,使用get和set函數封裝名字這個數據成員。在main函數中經過new實例化對象,並打印其相關函數。

4-3-StudentHeapInstance/main.cpp

#include <stdlib.h>
#include <iostream>
#include <string>
using namespace std;

/**
  * 定義類:Student
  * 數據成員:m_strName
  * 數據成員的封裝函數:setName()、getName()
  */
class Student
{
public:
    // 定義數據成員封裝函數setName()
    void setName(string _name) 
    { 
        m_strName = _name; 
    }
    
    
    // 定義數據成員封裝函數getName()
    string getName() const 
    {
        return m_strName; 
    }
    
//定義Student類私有數據成員m_strName
private:
    string m_strName;

};

int main()
{
    // 使用new關鍵字,實例化對象
    Student *str = new Student();
    // 設置對象的數據成員
    str->setName("mtianyan");
    // 使用cout打印對象str的數據成員
    cout << str ->getName() << endl;
    // 將對象str的內存釋放,並將其置空
    delete str;
    str = NULL;
    system("pause");
    return 0;
}

mark

類內定義與內聯函數

內聯函數關鍵字: inline

inline void fun()
{
    cout << "hello" << endl;
}

mark

  • 普通函數在調用時先找到函數體,執行完函數內容後再返回調用函數的語句;
  • 內聯函數inline在編譯時將函數體代碼和實參替換函數調用語句,執行速度更快,但只侷限於簡單的函數。

類內定義與類外定義

  • 類內定義是指在類裏面定義成員函數的函數體(優先編譯爲inline)

mark

mark

不會把inline寫出來,可是會優先編譯爲inline

  • 類外定義是指在類的外面先定義好這個函數如何運做,而後再經過類裏面聲名這個函數。

類外定義分爲兩種:

  • 同文件類外定義: 成員函數雖然定義在類的外面,可是它與類在同一個文件中定義
  • 分文件類外定義: 成員函數

同文件類外定義例子: Car.cpp

同文件類外定義

爲了標識這是屬於汽車car的成員函數:car::

同文件類外定義是突擊隊的話,分文件類外定義就是正規軍了。

幾乎全部的c++程序,專業點的程序員都會分文件類外定義。

分文件類外定義

一個.h頭文件,名稱與類名一致。必須包含.h文件,並使用car::

分文件類外定義

類外定義代碼演示

要求:

題目要求

5-2-1-OutClassDefine1/main.cpp

#include <stdlib.h>
#include <iostream>
#include <string>

using namespace std;
class Teacher {
public:
    void setName(string name);
    string getName();

    void setGender(std::string val);
    string getGender();

    void setAge(int _age);
    int getAge();

    void teach();
private:
    string m_strName;
    string m_strGender;
    int m_iAge;
};

string Teacher::getName()
{
    return m_strName;
}
void Teacher::setName(string name)
{
    m_strName = name;
}
string Teacher::getGender() 
{
 return m_strGender;
}
void Teacher::setGender(string val) 
{
 m_strGender = val; 
}
int Teacher::getAge() {
    return m_iAge;
}
void Teacher::setAge(int _age) {
    m_iAge = _age;
}
void Teacher::teach() {
    cout << "如今上課" << endl;
};
int main(void)
{
    Teacher teacher;
    teacher.setAge(21);
    teacher.setName("mtianyan");
    teacher.setGender("男");
    cout << teacher.getName() << " " << teacher.getGender() << " " << teacher.getAge() << endl;
    teacher.teach();
    system("pause");
    return 0;
}

mark

分文件類外定義實現。

5-2-2-MultipleFilesOutClassDefine/main.cpp

在解決方案,頭文件處右鍵添加新建項:頭文件: Teacher.h
在源文件右鍵添加新建項:cpp: Teacher.cpp

Teacher.h只存放類的聲明

#include <string>
using namespace std;

class Teacher {
public:
    void setName(string name);
    string getName();

    void setGender(std::string val);
    string getGender();

    void setAge(int _age);
    int getAge();

    void teach();
private:
    string m_strName;
    string m_strGender;
    int m_iAge;
};

Teacher.cpp:只存放類外方法的定義

#include "Teacher.h"
#include <iostream>
#include <string>
using namespace std;

string Teacher::getName()
{
    return m_strName;
}
void Teacher::setName(string name)
{
    m_strName = name;
}
string Teacher::getGender() 
{
    return m_strGender;
}
void Teacher::setGender(string val) 
{ 
    m_strGender = val; 
}
int Teacher::getAge() {
    return m_iAge;
}
void Teacher::setAge(int _age) {
    m_iAge = _age;
}
void Teacher::teach() {
    cout << "如今上課" << endl;
};

main.cpp存放類的實例化等以及程序主入口

#include <stdlib.h>
#include <iostream>
#include <string>
#include "Teacher.h"
using namespace std;

int main(void)
{
    Teacher teacher;
    teacher.setAge(21);
    teacher.setName("mtianyanMultiple");
    teacher.setGender("男");
    cout << teacher.getName() << " " << teacher.getGender() << " " << teacher.getAge() << endl;
    teacher.teach();
    system("pause");
    return 0;
}

mark

main.cpp的頭文件爲:

#include <stdlib.h>
#include <iostream>
#include "Teacher.h"
using namespace std;

Teacher.h的頭爲

#include <string>
using namespace std;

tercher.cpp的頭爲:

#include "Teacher.h"
#include <iostream>
#include <string>
using namespace std;

注意:不要把<string>寫成<string.h>

c++構造函數講解

  • 實例化的對象是如何在內存中存儲的?
  • 類中的代碼又是如何存儲的?
  • 數據與代碼的關係?

對象的結構

內存分區

mark

  • 棧區 : int x=0; int*p=NULL; 內存由系統管理。
  • 堆區 : int *p = new int[20]; new & delete
  • 全局區: 存貯全局變量及靜態變量
  • 常量區: string str = "hello";
  • 代碼區: 存貯編譯以後的二進制代碼

定義一個汽車類,在類被實例化以前,是不會佔用棧或者堆內存的。
可是當它實例化出car1 ,car2 ,car3 。每一個對象在棧上開闢一段內存,用來存儲各自的數據。不一樣的變量,佔據不一樣的內存。

代碼區只有一份代碼。

類實例化內存

對象初始化

mark

坦克大戰,遊戲開始初始化坦克位置。

對象初始化分爲兩種:

  • 有且僅有一次的初始化
  • 根據條件初始化

對於有且僅有一次的初始化:

  • 思考: 初始化函數如何避免誤操做?

如,咱們寫代碼忘記調用了初始化函數,重複調用了初始化函數。

構造函數的規則和特色:

  • 構造函數在對象實例化時被自動調用;只須要將初始化代碼寫在構造函數內。

構造函數在對象實例化時別調用,且僅被調用一次

  • 構造函數與類同名;
  • 構造函數沒有返回值;
  • 構造函數能夠被重載;遵循重載函數的規則。
  • 實例化對象時僅用一個構造函數;
  • 用戶沒有定義構造函數時,編譯器會自動生成一個構造函數(這個構造函數中沒有作任何事情)。

無參構造函數

class Student
{
public:
    Student(){
        m_strName = "jim";
    } // 與類名相同,無返回值。
private:
    string m_strName;
}

有參構造函數

class Student
{
public:
    Student(string name){
        m_strName = name;
    }
private:
    string m_strName;
}

重載構造函數

class Student
{
public:
    Student(){
        m_strName = "jim";
    }
    Student(string name){
        m_strName = name;
    }// 重載: 參數個數不一樣,參數類型不一樣,參數調用順序不一樣。
private:
    string m_strName;
}

C++構造函數代碼演示

要求

Teacher();
    Teacher(string name, int age=20);

這樣的計算機是能夠分辨的,可是若是給name也給默認值。那麼將沒法經過編譯。
提示重載函數的調用不明確。兩個不能共存,可是能夠單獨存在。

6-2-ConstructorFunction

Teacher.h

#include <string>
using namespace std;

class Teacher {
public:
    Teacher();
    Teacher(string name, int age=20);
    void setName(string name);
    string getName();
    void setAge(int _age);
    int getAge();
    void teach();
private:
    string m_strName;
    int m_iAge;
};

teacher.cpp

#include "Teacher.h"
#include <iostream>
#include <string>
using namespace std;

Teacher::Teacher() {
    m_strName = "jim";
    m_iAge = 5;
    cout << "Teacher()" << endl;
}

Teacher::Teacher(string name, int age) {
    m_strName = name;
    m_iAge = age;
    cout << "Teacher(string name, int age)" << endl;
}

string Teacher::getName()
{
    return m_strName;
}
void Teacher::setName(string name)
{
    m_strName = name;
}
int Teacher::getAge() {
    return m_iAge;
}
void Teacher::setAge(int _age) {
    m_iAge = _age;
}
void Teacher::teach() {
    cout << "如今上課" << endl;
};

main.cpp

#include <iostream>
#include <string>
#include "Teacher.h"
using namespace std;

int main(void)
{
    Teacher teacher; //無參數實例化,這裏能正常運行是由於咱們沒有給參數都加默認值。
    Teacher t2("merry", 15);//有參數實例化
    Teacher t3("james");//,構造函數有默認值20
    cout << teacher.getName() << " " << teacher.getAge() << endl;
    cout << t2.getName() << " " << t2.getAge() << endl;
    cout << t3.getName() << " " << t3.getAge() << endl;
    teacher.teach();
    system("pause");
    return 0;
}

mark

構造函數除了重載還能夠給參數賦默認值。不調用時,編譯能夠經過的。

默認構造函數

mark

不管從堆中仍是棧中實例化對象,都有一個特色是不用傳參數。

mark

這樣的構造函數能夠像如上圖所示定義,自己不帶參數。

mark

或帶了參數的同時攜帶着全部參數的默認值。

  • 把實例化時不須要傳遞參數的構造函數稱爲默認構造函數。上面的兩種(以及下面代碼),都是默認構造函數。
Student(){}
Student(string name = "jim");

構造函數初始化列表

class Student
{
public:
    Student():m_strName("jim"),m_iAge(10){} //構造函數初始化列表進行初始化
private:
    string m_strName;
    int m_iAge;

}

構造函數後面使用一個冒號隔開,對於多個數據成員初始化,中間要由逗號隔開。賦值要用括號。

初始化列表的特性

  • 初始化列表先於構造函數執行
  • 初始化列表只能用於構造函數
  • 初始化列表能夠同時初始化多個數據成員,而且效率高,速度快。

初始化列表的必要性:

思考: 表面看起來初始化列表的工做是能夠由構造函數代勞的,要它還有何用?

下面舉例說明。

計算圓,其中pi是一個常量,因此用const修飾。

class Circle
{
public:
    Circle(){m_dPi=3.14} //錯誤,給常量賦值
private:
    const double m_dPi;
}

沒法對於咱們的靜態成員變量在構造函數中賦初值,解決方案: 在初始化列表中賦初值。

class Circle
{
public:
    Circle():m_dPi(3.14){} // 正確,使用初始化列表
private:
    const double m_dPi;
}

C++初始化列表編碼

要求

定義有參默認構造函數,使用初始化列表初始化數據。

6-5-ParameterConstructorFunctionInitList

Teacher.h

#include <string>
using namespace std;

class Teacher {
public:
    Teacher(string name ="hi", int age=1,int m =100); // 有參默認構造函數
    void setName(string name);
    string getName();

    void setAge(int _age);
    int getAge();

    int getMax();
    void setMax(int m_iMax);
private:
    string m_strName;
    int m_iAge;
    const int m_iMax;

};

Teacher.cpp

#include "Teacher.h"
#include <iostream>
#include <string>
using namespace std;

Teacher::Teacher(string name, int age ,int m):m_strName(name),m_iAge(age),m_iMax(m)
{
    cout << "Teacher(string name, int age)" << endl;
}

string Teacher::getName()
{
    return m_strName;
}
void Teacher::setName(string name)
{
    m_strName = name;
}
int Teacher::getAge() {
    return m_iAge;
}
void Teacher::setAge(int _age) {
    m_iAge = _age;
}
int Teacher::getMax() {
    return m_iMax;
}
void Teacher::setMax(int m_iMax) {
    m_iMax = m_iMax;
}

main.cpp

#include <stdlib.h>
#include <iostream>
#include <string>
#include "Teacher.h"
using namespace std;

int main(void)
{
    Teacher teacher("merry",12,150);
    cout << teacher.getName() << " " << teacher.getAge() <<" "<<teacher.getMax()<< endl;
    system("pause");
    return 0;
}

mark

一個類能夠沒有默認構造函數, 有別的構造函數也能夠實例化對象。在構造函數聲明的時候加了默認值,就不須要在定義的時候加默認值。

常量若是不在初始化列表中初始化,會提示必須在初始值設定項列表中初始化

c++拷貝構造函數

mark

上圖,這是咱們定義的Student類

使用時。

int main()
{
    Student stu1;
    Student stu2 =stu1;
    Student stu3(stu1);
    return 0;
}

實例化了三個對象,但上述代碼只會執行一次構造函數內的代碼。

三次實例化調用了構造函數,但不是咱們本身定義的,而是拷貝構造函數。

實例化對象必定會調用構造函數,可是像上面代碼這種,將不使用咱們定義的構造函數,而使用拷貝構造函數。

拷貝構造函數:

定義格式: 類名(const 類名& 變量名)

Student(){
    m_strName = "jim";
}
Student(const Student& stu){

}

傳入一個引用。

  • 若是沒有自定義拷貝構造函數則系統自動生成一個默認的拷貝構造函數
  • 當採用直接初始化(stu3(stu1);)或者複製初始化(stu2 =stu1;)實例化對象時系統自動調用拷貝構造函數

構造函數總結:

  • 無參構造函數:必定爲默認構造函數
  • 有參構造函數:

    - 參數帶默認值。若是參數都帶默認值,那麼也將是默認構造函數
      - 參數無默認值

mark

系統自動生成的函數:

  • 普通構造函數
  • 拷貝構造函數

一旦咱們自行定義,系統就不會再生成了。

初始化列表:

  • 普通構造函數
  • 拷貝構造函數

C++拷貝構造函數代碼演示

要求

6-8-CopyConstructorFunction

Teacher.h

#include <string>
using namespace std;

class Teacher {
public:
    Teacher(string name ="mtianyan", int age=21,int m =100);
    Teacher(const Teacher& tea); //拷貝構造函數
    void setName(string name);
    string getName();
    void setAge(int _age);
    int getAge();
private:
    string m_strName;
    int m_iAge;
};

teacher.cpp

#include "Teacher.h"
#include <iostream>
#include <string>
using namespace std;

Teacher::Teacher(string name, int age ,int m):m_strName(name),m_iAge(age)
{
    cout << "Teacher(string name, int age)" << endl;
}
Teacher::Teacher(const Teacher& tea) {
    cout << "Teacher(const Teacher &tea)" << endl;
}
string Teacher::getName()
{
    return m_strName;
}
void Teacher::setName(string name)
{
    m_strName = name;
}
int Teacher::getAge() {
    return m_iAge;
}
void Teacher::setAge(int _age) {
    m_iAge = _age;
}

main.cpp

#include <stdlib.h>
#include <iostream>
#include <string>
#include "Teacher.h"
using namespace std;

void test(Teacher t) {

}

int main(void)
{
    Teacher teacher;
    Teacher t2 = teacher;
    Teacher t3(t2);  // 這裏不管使用t2仍是teacher都只會調用拷貝構造函數
    test(teacher); //函數使用已實例化的對象時調用。
    cout << teacher.getName() << " " << teacher.getAge() << endl;
    system("pause");
    return 0;
}

mark

函數使用已實例化的對象做爲參數時就會調用拷貝構造函數。如test()

拷貝構造函數的參數是肯定的,不能重載

析構函數

mark

構造函數是對象來到世界的第一聲哭泣
析構函數是對象離開世界的最後一聲嘆息。

析構函數在對象銷燬時會自動調用,歸還系統資源:

定義格式:

~類名() //不加任何的參數
class Student
{
public:
    Student(){cout << "Student" << endl;}
    ~Student(){cout << "~Student" << endl;}
}

析構函數不容許加任何參數。

析構函數的價值:

若是咱們在定義數據時使用了指針,使用指針指向了堆中分配的內存(new)。在對象銷燬時咱們要釋放這些內存,釋放這些內存最好時機是對象被銷燬以前。

class Student
{
public:
    Student(){m_pName = new char[20];}
    ~Student(){delete []m_pName;}
private:
    char *m_pName;
}
  • 析構函數沒有返回值,沒有參數也不能重載
  • 若是沒有自定義的析構函數則系統自動生成
  • 析構函數在對象銷燬時自動調用

對象的生命歷程:

mark

  1. 申請內存
  2. 初始化列表(此時其餘的值不肯定)
  3. 構造函數
  4. 參與運算
  5. 析構函數
  6. 釋放內存

C++析構函數代碼演示

要求

按回車後一瞬間能夠看到析構函數被調用

6-11-DestructorFunction

Teacher.h

#include <string>
using namespace std;

class Teacher {
public:
    Teacher(string name ="mtianyan", int age=21,int m =100); // 構造
    Teacher(const Teacher &tea); // 拷貝構造
    ~Teacher(); // 析構
    void setName(string name);
    string getName();
    void setAge(int _age);
    int getAge();
private:
    string m_strName;
    int m_iAge;

};

Teacher.cpp

#include "Teacher.h"
#include <iostream>
#include <string>
using namespace std;

Teacher::Teacher(string name, int age ,int m):m_strName(name),m_iAge(age)
{
    cout << "Teacher(string name, int age)" << endl;
}
Teacher::Teacher(const Teacher &tea) {
    cout << "Teacher(const Teacher &tea)" << endl;
}
Teacher::~Teacher() {
    cout << "~Teacher()" << endl;
}
string Teacher::getName()
{
    return m_strName;
}
void Teacher::setName(string name)
{
    m_strName = name;
}
int Teacher::getAge() {
    return m_iAge;
}
void Teacher::setAge(int _age) {
    m_iAge = _age;
}

main.cpp

#include <stdlib.h>
#include <iostream>
#include <string>
#include "Teacher.h"
using namespace std;

void test(Teacher t) {

}

int main(void)
{
    Teacher t1;
    Teacher t2(t1);
    Teacher *p = new Teacher();
    delete p;
    p = NULL;
    system("pause");
    return 0;
}

mark

在按下回車的瞬間,能夠看到兩行輸出以下。這是咱們t1 t2在銷燬時調用析構函數。

~Teacher()

堆棧中的對象在銷燬時都會自動調用析構函數。

總結

梳理前面學過的 & 劇透後面的

圍繞類與對象展開。

mark

類由成員函數和數據成員組成。擔憂本身的類與其餘人重名,類之上能夠定義命名空間。

mark

數據成員:

普通數據成員,(數據類型普通) int, char, char[], string;
初始化列表(const成員);靜態數據成員;對象成員

mark

成員函數:

對於數據成員進行封裝, 屬性封裝函數(get,set); 通常功能函數;特殊函數:構造函數(根據參數不一樣,拷貝構造函數-默認構造函數);析構函數.

mark

成員函數 (參數默認值;函數重載;引用;const;)

對象實例化(堆中實例化,棧中實例化)

mark

對象能夠是個引用?對象能夠用const修飾麼?

  • 成員函數中如何辨識數據成員。
  • 多個對象如何共享數據
  • 對象成員初始化

綜合練習:

定義一個Student類,包含名字一個數據成員,定義無參構造函數、有參構造函數、拷貝構造函數、析構函數及對於名字的封裝函數,在main函數中實例化Student對象,並訪問相關函數,觀察運行結果。

7-2-StudentDemo/main.cpp

#include <stdlib.h>
#include <iostream>
#include <string>
using namespace std;
/**
* 定義類:Student
* 數據成員:m_strName
* 無參構造函數:Student()
* 有參構造函數:Student(string _name)
* 拷貝構造函數:Student(const Student& stu)
* 析構函數:~Student()
* 數據成員函數:setName(string _name)、getName()
*/
class Student {
public:
    Student() {
        m_strName = "";
    };
    Student(string _name) {
        m_strName = _name;
    };
    Student(const Student& stu) {

    };
    ~Student() {

    };
    void setName(string _name) {
        m_strName = _name;
    };
    string getName() {
        return m_strName;
    };
private:
    string m_strName;
};

int main(void)
{
    // 經過new方式實例化對象*stu
    Student *stu = new Student();
    // 更改對象的數據成員爲「mtianyan」
    stu->setName("mtianyan");
    // 打印對象的數據成員
    cout << stu->getName() << endl;
    delete stu;
    stu = NULL;
    system("pause");
    return 0;
}

注意new方式實例化的對象不要忘記delete以及指針置空。

mark

相關文章
相關標籤/搜索