《C++文章匯總》
上一篇介紹了引用和匯編《07-內存管理、命名空間和繼承》,本文初始化列表、父類構造函數、虛函數和多態。
1.初始化列表:和構造函數的匯編代碼一樣
?特點
一種便捷的初始化成員變量的方式
只能用在構造函數中
初始化順序只跟成員變量的聲明順序有關
struct Person {
int m_age;
int m_height;
// Person(int age,int height){
// m_age = age;
// m_height = height;
// }
Person(int age,int height):m_age(age),m_height(height){
};
};
int main(){
Person person(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//輸出
18
180
初始化列表可以接收表達式和函數
int func(){return 8;}
struct Person {
int m_age;
int m_height;
Person(int age,int height):m_age(func()),m_height(height+2){
};
};
int main(){
Person person(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//輸出
8
182
賦值順序變化,初始化只與成員變量的定義順序有關
struct Person {
int m_age;
int m_height;
Person(int age,int height):m_age(m_height),m_height(height+2){
};
};
int main(){
Person person(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//輸出
32766
182
在構造函數中改變成員變量的賦值順序,結果并不會發生改變
struct Person {
int m_age;
int m_height;
Person(int age,int height):m_height(height+2),m_age(m_height){
};
};
int main(){
Person person(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//輸出
32766
182
如果改變m_age和m_height的定義順序,結果會改變
struct Person {
int m_height;
int m_age;
Person(int age,int height):m_height(height+2),m_age(m_height){
};
};
int main(){
Person person(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//輸出
182
182
通過函數調用能看到順序
int myAge(){
cout << "myAge()" << endl;
return 10;
};
int myHeight(){
cout << "myHeight()" << endl;
return 10;
};
struct Person {
int m_age;
int m_height;
Person(int age,int height):m_height(myHeight()),m_age(myAge()){
};
};
int main(){
Person person(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//輸出
myAge()
myHeight()
10
10
2.初始化列表與默認參數配合使用:相當于寫了三個構造函數
struct Person {
int m_age;
int m_height;
Person(int age = 1,int height = 125):m_age(age),m_height(height){
};
};
int main(){
Person person;
Person person0();//函數聲明
Person person1(17);
Person person2(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
cout << person1.m_age << endl;
cout << person1.m_height << endl;
cout << person2.m_age << endl;
cout << person2.m_height << endl;
return 0;
}
//輸出
1
125
17
125
18
180
初始化列表函數中的賦值,會在自動生成的賦值語句后面執行
struct Person {
int m_age;
int m_height;
Person(int age = 1,int height = 125):m_age(age),m_height(height){
m_age = 10;
};
};
int main(){
Person person;
Person person0();//函數聲明
Person person1(17);
Person person2(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
cout << person1.m_age << endl;
cout << person1.m_height << endl;
cout << person2.m_age << endl;
cout << person2.m_height << endl;
return 0;
}
//輸出
10
125
10
125
10
180
? 如果函數聲明和實現是分離的
初始化列表只能寫在函數的實現中
默認參數只能寫在函數的聲明中
struct Person {
int m_age;
int m_height;
Person(int age = 1,int height = 125);
};
Person::Person(int age,int height):m_age(age),m_height(height){
m_age = 10;
};
int main(){
Person person;
Person person0();//函數聲明
Person person1(17);
Person person2(18,180);
cout << person.m_age << endl;
cout << person.m_height << endl;
cout << person1.m_age << endl;
cout << person1.m_height << endl;
cout << person2.m_age << endl;
cout << person2.m_height << endl;
return 0;
}
//輸出
10
125
10
125
10
180
3.構造函數的互相調用
構造函數之間的互相調用在初始化列表中,在構造函數Person()中會將外面的person對象傳入到Person(10,20)中,Person(10,20)中改變的m_age,m_height的值就為外面的Person對象的成員變量的值
struct Person {
int m_age;
int m_height;
Person():Person(10,20){
//創建了一個臨時的person對象
Person(10,20);
// Person person;
// person.m_age = 10;
// person.m_height = 20;
}
Person(int age,int height){
m_age = age;
m_height = height;
}
};
int main(){
Person person;
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//輸出
10
20
? 注意:下面的寫法是錯誤的,初始化的是一個臨時對象,傳入的Person(int age,int height)方法中對象的地址是臨時對象的地址,傳入匯編,并不會改變外面person對象的成員變量m_age和m_height的值
struct Person {
int m_age;
int m_height;
Person(){
//創建了一個臨時的person對象
Person(10,20);
// Person person;
// person.m_age = 10;
// person.m_height = 20;
}
Person(int age,int height){
m_age = age;
m_height = height;
}
};
int main(){
Person person;
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
//輸出
-272632816
32766
4.父類的構造函數
? 子類的構造函數默認會調用父類的無參構造函數
struct Person{
int m_age;
Person(){
cout << "Person::Person()" << endl;
}
};
struct Student:Person{
int m_no;
Student(){
cout << "Student::Student()" << endl;
}
};
int main(){
Student student;
return 0;
}
//輸出
Person::Person()
Student::Student()
? 如果子類的構造函數顯式地調用了父類的有參構造函數,就不會再去默認調用父類的無參構造函數
struct Person{
int m_age;
Person(){
cout << "Person::Person()" << endl;
}
Person(int age){
cout << "Person::Person(int age)" << endl;
}
};
struct Student:Person{
int m_no;
Student():Person(10){
cout << "Student::Student()" << endl;
}
};
int main(){
Student student;
return 0;
}
//輸出
Person::Person(int age)
Student::Student()
? 如果父類缺少無參構造函數,子類的構造函數必須顯式調用父類的有參構造函數
struct Person{
int m_age;
Person(int age){
cout << "Person::Person(int age)" << endl;
}
};
struct Student:Person{
int m_no;
Student():Person(10){
cout << "Student::Student()" << endl;
}
};
int main(){
Student student;
return 0;
}
//輸出
Person::Person(int age)
Student::Student()
? 父類沒有構造函數,就不調用父類構造函數了
struct Person{
int m_age;
};
struct Student:Person{
int m_no;
Student(){
cout << "Student::Student()" << endl;
}
};
int main(){
Student student;
return 0;
}
//輸出
Student::Student()
? 父類子類成員變量同時初始化
class Person{
int m_age;
public:
Person(int age) :m_age(age){}
};
class Student:Person{
int m_no;
public:
Student(int no,int age) :m_no(no),Person(age){
cout << "Student::Student()" << endl;
}
};
int main(){
Student student(10,20);
return 0;
}
5.構造、析構順序
構造順序:先父類后子類
析構順序:先子類后父類
struct Person {
Person(){
cout << "Person::Person()" << endl;
};
~Person(){
cout << "Person::~Person()" << endl;
};
};
struct Student:Person{
Student(){
cout << "Student::Student()" << endl;
};
~Student(){
cout << "Student::~Student()" << endl;
};
};
int main(){
{
Student student;
}
return 0;
}
//輸出
Person::Person()
Student::Student()
Student::~Student()
Person::~Person()
6.多態
? 父類指針可以指向子類對象,是安全的,開發中經常用到(繼承方式必須是public)
struct Person {
int m_age;
};
struct Student:Person{
int m_score;
};
int main(){
Person *p = new Student();
p->m_age = 10;//安全區域
getchar();
return 0;
}
? 子類指針指向父類對象是不安全的:編譯不會報錯,編譯器只管是什么類型,運行時會報錯與否,與編譯器的容錯能力有關,Xcode會報錯error: 'm_score' is a private member of 'Student
struct Person {
int m_age;
};
struct Student:Person{
int m_score;
};
int main(){
// Person *p = new Student();
// p->m_age = 10;//安全區域
Student *p = (Student *)new Person();
p->m_age = 10;
p->m_score = 99;//不安全,實質上p指針指向的地址Person對象里面并不存在m_score的區域,p->m_score指向的是未知區域,可能誤將別人的數據抹掉或篡改掉
getchar();
return 0;
}
若是class類,則繼承方式默認為private,父類的指針就不能指向子類的對象了,此時在父類前要加上public
class Person {
public:
int m_age;
};
class Student: public Person{
int m_score;
};
int main(){
Person *p = new Student();
p->m_age = 10;//安全區域
getchar();
return 0;
}
? 默認情況下,編譯器只會根據指針類型調用對應的函數,不存在多態
? 多態是面向對象非常重要的一個特性
同一操作作用于不同的對象,可以有不同的解釋,產生不同的執行結果
在運行時,可以識別出真正的對象類型,調用對應子類中的函數
struct Animal{
void speak(){
cout << "Animal::speak()" << endl;
}
void run(){
cout << "Animal::run()" << endl;
}
};
struct Dog:Animal{
void speak(){
cout << "Dog::speak()" << endl;
}
void run(){
cout << "Dog::run()" << endl;
}
};
struct Cat:Animal{
void speak(){
cout << "Cat::speak()" << endl;
}
void run(){
cout << "Cat::run()" << endl;
}
};
struct Pig:Animal{
void speak(){
cout << "Pig::speak()" << endl;
}
void run(){
cout << "Pig::run()" << endl;
}
};
void liu(Animal *p){
p->speak();
p->run();
}
int main(){
liu(new Dog());
liu(new Cat());
liu(new Pig());
Cat *p = (Cat *)new Dog();
p->speak();//call Cat::speak
p->run();//call Cat::run
return 0;
}
//輸出
Animal::speak()
Animal::run()
Animal::speak()
Animal::run()
Animal::speak()
Animal::run()
Cat::speak()
Cat::run()
查看匯編發現,在調用函數時會直接在函數面前加上指針類型,指針類型為Animal *,故全部打印為Animal,那么C++如何實現多態呢?
? 多態的要素
子類重寫父類的成員函數(override)
父類指針指向子類對象
利用父類指針調用重寫的成員函數
7.虛函數
? C++中的多態通過虛函數(virtual function)來實現
? 虛函數:被virtual修飾的成員函數
? 只要在父類中聲明為虛函數,子類中重寫的函數也自動變成虛函數(也就是說子類中可以省略virtual關鍵字)
struct Animal{
virtual void speak(){
cout << "Animal::speak()" << endl;
}
virtual void run(){
cout << "Animal::run()" << endl;
}
};
struct Dog:Animal{
void speak(){
cout << "Dog::speak()" << endl;
}
void run(){
cout << "Dog::run()" << endl;
}
};
struct Cat:Animal{
void speak(){
cout << "Cat::speak()" << endl;
}
void run(){
cout << "Cat::run()" << endl;
}
};
struct Pig:Animal{
void speak(){
cout << "Pig::speak()" << endl;
}
void run(){
cout << "Pig::run()" << endl;
}
};
void liu(Animal *p){
p->speak();
p->run();
}
int main(){
liu(new Dog());
liu(new Cat());
liu(new Pig());
Cat *p = (Cat *)new Dog();
p->speak();//call Cat::speak
p->run();//call Cat::run
return 0;
}
//輸出
Dog::speak()
Dog::run()
Cat::speak()
Cat::run()
Pig::speak()
Pig::run()
Dog::speak()
Dog::run()