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