C++继承与虚函数
继承顺序
派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。
我们可以根据访问权限总结出不同的访问类型,如下所示:
| 访问 | public | protected | private |
|---|---|---|---|
| 同一个类 | yes | yes | yes |
| 派生类 | yes | yes | no |
| 外部的类 | yes | no | no |
一个派生类继承了所有的基类方法,但下列情况除外:
- 基类的构造函数、析构函数和拷贝构造函数。
- 基类的重载运算符。
- 基类的友元函数。
#include <iostream>
using namespace std;
class Base{
public:
int a;
int b;
Base(int a=0, int b=0){
this->a = a;
this->b = b;
cout<<"Constructor Base::Base("<<a<<","<<b<<")"<<endl;
}
~Base(){
cout<<"destructor Base..."<<endl;
}
int product(){
return this->a * this->b;
}
friend std::ostream &operator<<(std::ostream &os, const Base &obj){
os<<"Base: a="<<obj.a<<",b="<<obj.b;
return os;
}
};
class Derived: public Base{
public:
int c;
// 继承Base类,会首先自动调用Base的构造函数
// 然后再去执行子类的构造函数
Derived(int c):Base(c-2, c-1), c(c){
// 这个a来自父类Base,在父类构造函数之后修改
this->a += 3;
cout<<"constructor Derived("<<c<<")"<<endl;
}
~Derived(){
cout<<"destructor Derived("<<c<<")"<<endl;
}
int product(){
// 子类调用父类的product
return Base::product() * c;
}
friend std::ostream &operator<<(std::ostream &os, const Derived &obj){
//
os<<static_cast<const Base&>(obj) << endl;
os<<"Derived: c="<<obj.c;
return os;
}
};
int main(int argc, const char** argv) {
Base base(1,2);
cout<<base<<endl;
int r = base.product();
cout<<r<<endl;
cout<<"------------------"<<endl;
Derived derive(5);
// 会调用Base(0, 1)
cout<<derive<<endl;
cout<<"product"<<derive.product()<<endl;
return 0;
}
虚函数
C++虚函数是多态性实现的重要方式,当某个虚函数通过指针或者引用调用时,编译器产生的代码直到运行时才能确定到底调用哪个版本的函数。被调用的函数是与绑定到指针或者引用上的对象的动态类型相匹配的那个。因此,借助虚函数,我们可以实现多态性。这也是OOP的核心思想之一。
下面是示例:
#include <iostream>
#include <string>
using namespace std;
class Person{
public:
string name;
Person(string name): name(name){}
void print(){
// virtual void print(){
cout<<"Name :"<<name<<endl;
}
};
class Student:public Person{
public:
string id;
Student(string n, string i): Person(n), id(i){}
void print(){
cout<<"Name: "<<name<<endl;
cout<<"Id: "<<id<<endl;
}
};
void printObjectInfo(Person &p){
p.print();
}
int main(int argc, const char** argv) {
{
Student stu("yu", "2000");
// 会调用父类的print函数
printObjectInfo(stu);
}
{
Person *p = new Student("ssss", "2000");
// 会调用父类的print函数
printObjectInfo(*p);
// 也会调用父类的print函数
p->print();
delete p;
}
// 初衷是针对不同的对象调用不同的函数,但是现在都是调用父类的函数
// 不过也合理,因为不论是printObjectInfo传入的就是Person类型,
// 还是p->print();p本身就是Person类型
// 但是如何改进它?
// 只需要在父类函数声明时加上关键字virtual
// 虚函数的作用实际是改变对象的绑定逻辑是静态绑定还是动态绑定
// 虚函数就会动态绑定
// 虚函数也可以不包含实现
// virtual void print() = 0;
// 补充: 析构函数一定是虚函数,析构销毁的一定是子类
}

浙公网安备 33010602011771号