C++繼承和多態

繼承

語法

class 子類名 : public 父類名 {

};

/* 示例 */ 
class Person {
};

// Student繼承自Person
class Student : public Person {
};

子類對象的內存空間

  • 一個類對象的內存空間 = 全部成員變量所佔空間的總和ios

  • 一個子類對象的內存空間 = 全部父類成員變量所佔空間的總和 + 自身成員變量所佔空間的總和ide

  • 對象所佔空間不包含靜態成員變量函數

  • 子類中,父類成員變量在內存中的位置靠前測試

  • 類中存在虛函數時,會多出一個指針的空間存放虛函數表的地址,而這個指針位於對象開頭this

#include <iostream>

using namespace std;

class Person {
    int age;
};

class Student : public Person {
    int id;
};

int main() {
    cout << sizeof(Student) << endl; // 8,兩個int
    cout << sizeof(Person) << endl; // 4,1個int
    return 0;
}

調用父類構造方法

採用初始化列表spa

#include <iostream>

using namespace std;

class Person {
public:
    int age;
    Person(int age) : age(age) {
        
    }
};

class Student : public Person {
public:
    int id;
    Student(int id, int age) : Person(age), id(id) {
        
    }
};

int main() {
    Student student(1, 25);
    cout << student.id << endl;
    cout << student.age << endl;
    return 0;
}

子類建立與消亡的順序

建立:父類 -> 子類
消亡:子類 -> 父類指針

#include <iostream>

using namespace std;

class Person {
public:
    Person() {
        cout << "Person()" << endl;
    }
    ~Person() {
        cout << "~Person()" << endl;
    }
};

class Student : public Person {
public:
    Student() {
        cout << "Student()" << endl;
    }
    ~Student() {
        cout << "~Student()" << endl;
    }
};

int main() {
    Student student;
    return 0;
}

重載時調用父類方法

父類名::方法名();code

#include <iostream>

using namespace std;

class Person {
public:
    int age;
    
    void sayHello() {
        cout << "Hello" << endl;
    }
};

class Student : public Person {
public:
    int id;
    
    void sayHello() {
        Person::sayHello();
        cout << "World" << endl;
    }
};

int main() {
    Student student;
    student.sayHello();
    return 0;
}

多態

多態就是父類的指針或引用調用子類的方法或變量對象

#include <iostream>

using namespace std;

class Person {
public:
    void sayHi() {
        cout << "Person sayHi" << endl;
    }
};

class Student : public Person {
public:
    void sayHi() {
        cout << "Student sayHi" << endl;
    }
};

int main() {
    Student student;
    
    Person * pPerson = &student;
    pPerson->sayHi(); // 打印:Person sayHi
    
    Person & rPerson = student;
    rPerson.sayHi(); // 打印:Person sayHi

    return 0;
}

滿心期待上面的代碼可以完成多態,結果...尼瑪怎麼不對!!!繼承

原來坑爹C++又整出個虛函數

虛函數

  • 一個正常的成員函數前加virtual關鍵字

  • 若是該方法在父類中已經定義爲虛函數,那麼子類virtual能夠省略,但不建議

#include <iostream>

using namespace std;

class Person {
public:
    void sayHi() {
        cout << "Person sayHi" << endl;
    }
    virtual void sayHello() {
        cout << "Person sayHello" << endl;
    }
};

class Student : public Person {
public:
    void sayHi() {
        cout << "Student sayHi" << endl;
    }
    void sayHello() {
        cout << "Student sayHello" << endl;
    }
};

int main() {
    Student student;
    
    Person * pPerson = &student;
    pPerson->sayHi();
    pPerson->sayHello();
    
    // 引用方式調用多態
//    Person & rPerson = student;
//    rPerson.sayHi();
//    rPerson.sayHello();
    
    return 0;
}
  • 構造函數和析構函數中,能夠調用虛函數,但不會出現多態。緣由是父類構造函數調用時,子類成員變量還未初始化,父類析構函數調用時,子類析構函數已經執行完畢。總之若是能夠在構造和析構函數中出現多態,是危險的。

#include <iostream>

using namespace std;

class Person {
public:
    Person() {
        sayHello();
    }
    virtual void sayHello() {
        cout << "Person sayHello" << endl;
    }
    // 析構函數測試
//    ~Person() {
//        sayHello();
//    }
};

class Student : public Person {
public:
    Student() {
        sayHello();
    }
    virtual void sayHello() {
        cout << "Student sayHello" << endl;
    }
//    ~Student() {
//        sayHello();
//    }
};

int main() {
    Student student;
    return 0;
}
  • 當父類指針或引用調用不是虛函數的成員函數,這個成員函數中又調用了虛函數,會出現多態。

#include <iostream>

using namespace std;

class Person {
public:
    void sayHi() {
        cout << "Person sayHi" << endl;
        sayHello(); // 至關於this->sayHello(); 跟在外部用指針調用是一個意思
    }
    virtual void sayHello() {
        cout << "Person sayHello" << endl;
    }
};

class Student : public Person {
public:
    void sayHi() {
        cout << "Student sayHi" << endl;
        sayHello();
    }
    virtual void sayHello() {
        cout << "Student sayHello" << endl;
    }
};

int main() {
    Student student;
    
    Person * pPerson = &student;
    pPerson->sayHi();
    
    return 0;
}

多態的實現原理

  • 多態實現的關鍵是虛函數表

  • 每個有虛函數的類(或是父類中有虛函數) 都有一個虛函數表

  • 該類的對象在內存的開頭,存放着一個指針,這個指針指向虛函數表

  • 虛函數表中列出了該類的虛函數地址

#include <iostream>

using namespace std;

class Person {
public:
    virtual void sayHello() {
    
    }
};

int main() {
    // 跟一個指針的大小是同樣的
    cout << sizeof(Person) << endl;
    return 0;
}

虛析構函數

爲何須要虛析構函數

當用父類指針或引用,delete時,只有父類的析構函數會被調用。定義虛析構函數能夠解決這個問題。

#include <iostream>

using namespace std;

class Person {
public:
    ~Person() {
        cout << "~Person" << endl;
    }
};

class Student : public Person {
public:
    ~Student() {
        cout << "~Student" << endl;
    }
};

int main() {
    Person * person = new Student;
    delete person;
    return 0;
}

定義虛析構函數

  • 在析構函數前加virtual關鍵字

  • 若是父類的析構函數已經加了virtual,子類中的virtual關鍵字能夠省略,不推薦

#include <iostream>

using namespace std;

class Person {
public:
    Person() {
        cout << "Person" << endl;
    }
    virtual ~Person() {
        cout << "~Person" << endl;
    }
};

class Student : public Person {
public:
    Student() {
        cout << "Student" << endl;
    }
    ~Student() {
        cout << "~Student" << endl;
    }
};

int main() {
    Person * person = new Student;
    delete person;
    return 0;
}

純虛函數和抽象類

  • 純虛函數: 沒有函數體的虛函數

  • 抽象類: 包含純虛函數的類

  • 非抽象類: 實現全部純虛函數的類

定義

virtual 返回值 函數名(參數列表) = 0 ;

示例

#include <iostream>

using namespace std;

class CGeometry {
public:
    virtual ~CGeometry(){
        
    }
    virtual double calPerimeter() = 0;
};

class CTriangle : public CGeometry {
public:
    double side1;
    double side2;
    double side3;
    
    virtual double calPerimeter() {
        return side1 + side2 + side3;
    }
};

class CCircle : public CGeometry {
public:
    double radius;
    
    virtual double calPerimeter() {
        return 2 * 3.14 * radius;
    }
};

class CRectangle : public CGeometry {
public:
    double width;
    double height;
    
    virtual double calPerimeter() {
        return 2 * (width + height);
    }
};

int compare(const void * p1, const void * p2) {
    int result = 0;
    
    CGeometry ** g1 = (CGeometry **)p1;
    CGeometry ** g2 = (CGeometry **)p2;
    
    double perimeter1 = (*g1)->calPerimeter();
    double perimeter2 = (*g2)->calPerimeter();
    
    if (perimeter1 > perimeter2) {
        result = 1;
    } else if (perimeter1 < perimeter2) {
        result = -1;
    }
    
    return result;
}

/*
 運行:前四行爲輸入,後3行爲輸出
 3
 R 1 1
 C 1
 T 1 1 1
 3
 4
 6.28
 */
int main() {
    
    int number;
    cin >> number;
    
    char type;
    CGeometry * geometries[number];
    
    CTriangle * pTriangle;
    CCircle * pCircle;
    CRectangle * pRectangle;
    
    for (int i = 0; i < number; i++) {
        cin >> type;
        
        switch (type) {
            case 'R':
                pRectangle = new CRectangle();
                cin >> pRectangle->width >> pRectangle->height;
                geometries[i] = pRectangle;
                break;
            case 'C':
                pCircle = new CCircle();
                cin >> pCircle->radius;
                geometries[i] = pCircle;
                break;
            case 'T':
                pTriangle = new CTriangle();
                cin >> pTriangle->side1 >> pTriangle->side2 >> pTriangle->side3;
                geometries[i] = pTriangle;
                break;
        }
    }
    
    qsort(geometries, number, sizeof(CGeometry *), compare);
    
    for (int i = 0; i < number; i++) {
        cout << geometries[i]->calPerimeter() << endl;
        delete geometries[i];
    }
    
    return 0;
}
相關文章
相關標籤/搜索