1. 程式人生 > 實用技巧 >c++3(c++2的補充)

c++3(c++2的補充)

1深拷貝和淺拷貝(編譯器自帶淺拷貝)

其實聽名字也知道,拷貝拷貝,就是複製過來,一模一樣賦值,

1. 使用一個已經建立完畢的物件來初始化一個新物件

Person man(100); //p物件已經建立完畢
    Person newman(man); //呼叫拷貝建構函式

//2. 值傳遞的方式給函式引數傳值 //相當於Person p1 = p;

作為引數的時候(我們知道作為引數的時候其實本質也是複製一份傳遞過去,所以經常發現如果沒有地址符&你就算怎樣改變形參引數的值,本質也不會更改)

//3. 以值方式返回區域性物件 P

//3. 以值方式返回區域性物件
Person doWork2()
{
    Person p1;
    cout 
<< (int *)&p1 << endl; return p1; }

//拷貝建構函式
    Person(const Person& p) {
        age = p.age;
        cout << "拷貝建構函式!" << endl;
    }

所以我們就可以這樣操作

Person p2(p1);

但是要清楚

//如果使用者提供有參構造,編譯器不會提供預設構造,會提供拷貝構造
    Person p1; //此時如果使用者自己沒有提供預設構造,會出錯
    Person p2(10); //使用者提供的有參
Person p3(p2); //此時如果使用者沒有提供拷貝構造,編譯器會提供 //如果使用者提供拷貝構造,編譯器不會提供其他建構函式 Person p4; //此時如果使用者自己沒有提供預設構造,會出錯 Person p5(10); //此時如果使用者自己沒有提供有參,會出錯 Person p6(p5); //使用者自己提供拷貝構造

這是拷貝的基礎知識,那麼什麼是深淺拷貝呢

淺拷貝:簡單的賦值拷貝操作

深拷貝:在堆區重新申請空間,進行拷貝操作

淺拷貝其實看上面的基礎就大概差不多是淺拷貝一些應用,

主要看看深拷貝的情況,如指標,我們知道,指標實質上就是一個區域性變數在堆區,但是用指標的時候需要分配記憶體空間需要new這時候就在棧區了(由程式設計師自己控制的區)

如下

//拷貝建構函式  
    Person(const Person& p) {
        cout << "拷貝建構函式!" << endl;
        //如果不利用深拷貝在堆區建立新記憶體,會導致淺拷貝帶來的重複釋放堆區問題
        m_age = p.m_age;
        m_height = new int(*p.m_height);
        
    }

    //解構函式
    ~Person() {
        cout << "解構函式!" << endl;
        if (m_height != NULL)
        {
            delete m_height;
        }
    }
public:
    int m_age;
    int* m_height;
};

如果不new一個空間深拷貝的話,那看看淺拷貝(即編譯器自帶的)會幹嘛,本質上是:

指標由於是拷貝,指向的地址也是一樣,造成解構函式釋放的時候,前面一個析構釋放了,後面一個又釋放

所以這就是淺拷貝帶來的問題,堆區的記憶體重複釋放

2 this指標

this一般就是指所在的類,用途是

  • 當形參和成員變數同名時,可用this指標來區分
  • 在類的非靜態成員函式中返回物件本身,可使用return *thiclass Person
{
public:

    Person(int age)
    {
        //1、當形參和成員變數同名時,可用this指標來區分
        this->age = age;
    }
    //返回的本體則需要引用的方式返回
    Person& PersonAddPerson(Person p)
    {
        this->age += p.age;
        //返回物件本身
    //在這裡this作為值返回時因為本身就是一個指標,再加*即*this那麼意義就是本體的意思了即下面的p2
return *this; } int age; }; void test01() { Person p1(10); cout << "p1.age = " << p1.age << endl; Person p2(10); p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1); cout << "p2.age = " << p2.age << endl; } int main() { test01(); system("pause"); return 0; }

3動態多型

這裡需要一個關鍵字virtual

為什麼是virtual,函式前面加上virtual關鍵字,變成虛擬函式,那麼編譯器在編譯的時候就不能確定函式呼叫了。

看下面的程式碼

class Animal
{
public:
    //Speak函式就是虛擬函式
    //函式前面加上virtual關鍵字,變成虛擬函式,那麼編譯器在編譯的時候就不能確定函式呼叫了。
    virtual void speak()
    {
        cout << "動物在說話" << endl;
    }
};

class Cat :public Animal
{
public:
!!!!!!!// 這裡可加可不加virtual 結果一樣!!!!!!!!!!!!!!
void speak() { 
cout
<< "小貓在說話" << endl;
}
};


class Dog :public Animal {
public:
  !!!!!!!// 這裡可加可不加virtual 結果一樣!!!!!!!!!!!!!!
void speak() { cout << "小狗在說話" << endl; } }; //我們希望傳入什麼物件,那麼就呼叫什麼物件的函式 //如果函式地址在編譯階段就能確定,那麼靜態聯編 //如果函式地址在執行階段才能確定,就是動態聯編 void DoSpeak(Animal & animal) { animal.speak(); } // //多型滿足條件: //1、有繼承關係 //2、子類重寫父類中的虛擬函式 //多型使用: //父類指標或引用指向子類物件 void test01() { Cat cat; DoSpeak(cat); Dog dog; DoSpeak(dog); } int main() { test01(); system("pause"); return 0; }

首先在c++中。。。。父類的引用或指標 。。。。。就是可以代表一家之主的意思當然可以代表這個家的地址,有了家的地址當然可以用家中的子類,父子其實就是一體。

void DoSpeak(Animal & animal)
DoSpeak(cat);
DoSpeak(dog);

要想讓貓為引數貓說話,讓狗狗說話就需要地址在編譯時沒有繫結,所以不加virtual的話,不管傳什麼都會是呼叫Animal的spreak結果都會是動物在說話

所以要在編譯前不繫結,即晚繫結,加virtual。那麼這個原理是什麼

virtual