C++中的继承与多继承
继承
可以让子类拥有父类的所有可访问成员(变量/函数)。
C++没有像Java的基类。
语法
在类名后面写个:再接上父类类名即可。Student : Person
struct Person
{
int id;
void run(){ }
};
struct Student : Person
{
int score;
void study() { }
};
struct Worker : Person
{
int salary;
void work() { }
};
关系描述:Person:父类(superclass,超类),Student:子类(subclass,派生类)。
内存布局:像上面的代码,Wroker这个类型的对象占用8个字节,以为父类占4个字节,自己占4个字节。父类的成员变量在前面,子类的在后面。
成员访问权限
成员访问权限、继承方式有3种
public:公共的,任何地方都可以访问(struct默认)。
protected:子类内部、当前类内部可以访问。
private:私有的,只有当前类内部可以访问(class默认)。
访问权限不影响对象的内存布局。
继承方式
可以通过在写继承关系的时候,指定访问权限,来决定子类是否可以访问这个类的父类成员。
class Person
{
private:
int id;
public:
void run(){ }
int get_id()
{
return this->id;
}
};
class Student : private Person // Student的子类无法访问Person类的成员
{
private:
int score;
public:
void study()
{
cout << this->get_id() << endl;
}
};
class GoodStudent : Student
{
void test()
{
// cout << this->get_id() << endl; 不可以访问,因为父类继承他父类的时候给权限是private
}
};
子类内部访问父类成员的权限,是成员本身的访问权限与上一级父类的继承方式中权限最小的那个。
默认访问权限
struct的默认访问权限是public,继承方式默认是public。不写默认是:struct A : public B {};。
class的默认访问权限是private,继承方式默认是private。不写默认是:class A : private B{};。
这个和父类用struct定义还是class定义没有关系。
struct Person
{
int id;
void run(){ }
};
class Student : Person
{
private:
int score;
public:
void study()
{
this->run();
}
};
class C : Student
{
void test()
{
this->run(); // 无法访问。
}
};
父类的构造函数
子类的构造函数默认会调用父类的无参构造函数。
如果子类的构造函数显式地调用了父类的有参构造函数,就不会再去默认调用父类的无参构造函数。
显式的调用父类的有参构造函数:
struct A
{
int a;
int b;
A() :A(10, 20)
{
}
A(int a, int b)
{
this->a = a;
this->b = b;
}
};
struct B : A
{
int a;
int b;
B() :A(1, 2)
{
}
};
如果父类缺少无参构造函数,子类的构造函数必须显式调用父类的有参构造函数。
struct A
{
int a;
int b;
A(int a, int b)
{
this->a = a;
this->b = b;
}
};
struct B : A
{
int a;
int b;
B() :A(1, 2)
{
}
};
如果父类没有写无参构造函数,就不会自动调用父类的构造函数。
构造/析构的顺序
struct A
{
A()
{
printf("A()\n");
}
~A()
{
printf("~A()\n");
}
};
struct B : A
{
B()
{
printf("B()\n");
}
~B()
{
printf("~B()\n");
}
};
int main()
{
{
B b;
}
return 0;
}
// A()
// B()
// ~B()
// ~A()
多继承
C++允许一个类可以有多个父类(不建议使用,会增加程序设计复杂度)。
struct Student
{
int score;
void study()
{
printf("Student::study() -- score = %d\n", this->score);
}
};
struct Worker
{
int salary;
void work()
{
printf("Worker::work() -- salary = %d\n", this->salary);
}
};
struct Undergraduate : Student, Worker
{
int grade;
void play()
{
printf("Undergraduate::play() -- grade = %d\n", this->grade);
}
};
父类成员变量哪个在前面只跟继承的顺序有关(内存空间中)。
构造函数的调用
struct Student
{
int score;
Student(int score) : score(score) {}
void study()
{
printf("Student::study() -- score = %d\n", this->score);
}
};
struct Worker
{
int salary;
Worker(int salary) : salary(salary) {}
void work()
{
printf("Worker::work() -- salary = %d\n", this->salary);
}
};
struct Undergraduate : Student, Worker
{
int grade;
Undergraduate(int score, int salary, int grade)
: Student(score)
, Worker(salary)
, grade(grade) {}
void play()
{
printf("Undergraduate::play() -- grade = %d\n", this->grade);
}
};
虚函数
如果子类继承的多个父类都有虚函数,那么子类对象就会产生对应的多张虚表。
struct Student
{
virtual void study()
{
printf("Student::study()\n");
}
};
struct Worker
{
virtual void work()
{
printf("Worker::work()\n");
}
};
struct Undergraduate : Student, Worker
{
void study()
{
printf("Undergraduate::study()\n");
}
void work()
{
printf("Undergraduate::work()\n");
}
void play()
{
printf("Undergraduate::play()\n");
}
};

同名函数
struct Student
{
void eat()
{
printf("Student::eat()\n");
}
};
struct Worker
{
void eat()
{
printf("Worker::eat()\n");
}
};
struct Undergraduate : Student, Worker
{
void eat()
{
printf("Undergraduate::eat()\n");
}
};
int main()
{
Undergraduate u;
u.eat(); // Undergraduate::eat()
u.Student::eat(); // Student::eat()
u.Worker::eat(); // Worker::eat()
u.Undergraduate::eat(); // Undergraduate::eat()
return 0;
}
同名成员变量
struct Student
{
int age;
};
struct Worker
{
int age;
};
struct Undergraduate : Student, Worker
{
int age;
};
int main()
{
Undergraduate u;
u.age = 10;
u.Student::age = 20;
u.Worker::age = 30;
// Student::age = 20, Worker::age = 30, Undergraduate::age = 10
printf("Student::age = %d, Worker::age = %d, Undergraduate::age = %d",
u.Student::age, u.Worker::age, u.Undergraduate::age);
return 0;
}

菱形继承


struct Person
{
int age;
};
struct Student : Person
{
int score;
};
struct Worker : Person
{
int salary;
};
struct Undergraduate : Student, Worker
{
int grade; // age被重复继承了
};
int main()
{
Undergraduate g;
printf("%d", sizeof(g)); // 20
return 0;
}

问题
最底下子类从基类继承的成员变量冗余、重复。
最底下子类无法访问基类的成员,有1二义性。
解决:虚继承
虚继承可以解决菱形继承带来的问题。

Person类被称为虚基类。
struct Person
{
int age = 1;
};
struct Student : virtual Person
{
int score = 2;
};
struct Worker : virtual Person
{
int salary = 3;
};
struct Undergraduate : Student, Worker
{
int grade = 4;
};
