C++继承和多态

更新于 2015-12-14  约 16 分钟

继承

语法

class 子类名 : public 父类名 {

};

/* 示例 */ 
class Person {
};

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

子类对象的内存空间

  • 一个类对象的内存空间 = 所有成员变量所占空间的总和

  • 一个子类对象的内存空间 = 所有父类成员变量所占空间的总和 + 自身成员变量所占空间的总和

  • 对象所占空间不包含静态成员变量

  • 子类中,父类成员变量在内存中的位置靠前

  • 类中存在虚函数时,会多出一个指针的空间存放虚函数表的地址,而这个指针位于对象开头

#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;
}

调用父类构造方法

采用初始化列表

#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;
}

重载时调用父类方法

父类名::方法名();

#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;
}
阅读 1.8k更新于 2015-12-14

推荐阅读
人工智能之路
用户专栏

一只笨鸟的人工智能之路

5 人关注
26 篇文章
专栏主页
目录