多级继承
一个派生类继承了基类除构造函数,拷贝构造函数,析构函数,重载运算符和友元函数所有的成员函数和成员变量。
#include <iostream>
using namespace std;
class A {
public:
void set_width(int w);
void set_height(int h);
void set_length(int l);
void set_name(char *n);
void set_sport(char *st);
char *get_sport();
protected:
int height;
int width;
char *name;
int length;
private:
char *sport;
};
void A::set_width(int w) { width = w; }
void A::set_height(int h) { height = h; }
void A::set_length(int l) { length = l; }
void A::set_name(char *n) { name = n; }
void A::set_sport(char *st) { sport = st; }
char *A::get_sport() { return sport; }
class B : public A {
public:
void set_score(int scores);
protected:
int score;
};
void B::set_score(int scores) { score = scores; }
class C : public B {
public:
void display();
};
void C::display() {
cout << height << width << length << name << get_sport() << score << endl;
}
int main() {
system("chcp 65001");
C c;
char name[] = "阿吉";
char sport[] = "健身";
c.set_height(10);
c.set_width(20);
c.set_length(30);
c.set_score(50);
c.set_sport(sport);
c.set_name(name);
c.display();
return 0;
}
输出:
Active code page: 65001
102030阿吉健身50
继承有单继承和多继承,在java中只能够单继承,也就是只能继承一个基类,而在C++中可以多重继承,也就是继承多个基类,上述程序中A就是一个基类,而B是A的派生类,C是B的派生类,因为B继承于A,所以C也是A的派生类。派生类可以继承除private修饰符以外的所有成员变量和成员函数。继承形式: class X : public(private或protected)Y {};
Y前面的修饰符指定了派生类X中的访问级别,当继承基类是public的时候,基类中的public在派生类中按照public访问,protected按照protected访问,private不允许访问,也就是在派生类中隐藏了。 若是protected,那么public在派生类中按照protected访问(权限降低),protected按照protected访问,private不允许访问。若是private,那么public在派生类中按照private访问(权限降到最低),protected按照private访问,private不能访问。可以看到无论继承基类的修饰符是public,protected,还是private的,基类中声明的private的成员变量和成员函数都不能访问,在派生类中隐藏。
调用基类的构造函数
#include <iostream>
using namespace std;
class Teacher {
public:
Teacher(char *name, int age);
protected:
char *name;
int age;
};
Teacher::Teacher(char *name, int age) : name(name), age(age) {
}
class Student : public Teacher {
public:
Student(char *name, int age, float score);
void display();
private:
float score;
};
Student::Student(char *name, int age, float score) : Teacher(name, age), score(score) {}
void Student::display() {
cout << name << "的年龄是" << age << ",成绩是" << score << endl;
}
int main() {
system("chcp 65001");
char name[] = "阿吉";
int age = 20;
int score = 100;
Student stu(name, age, score);
stu.display();
return 0;
}
输出:
Active code page: 65001
阿吉的年龄是20,成绩是100
派生类构造函数总是先调用基类构造函数再执行其他代码(包括参数初始化以及函数体的代码),参数初始化指的是this.score = score;
子类中的成员变量初始化。注意基类构造函数不会被继承,不能当做普通的成员函数来调用。换句话说,只能将基类构造函数的调用放在函数头部,不能放在函数体中。另外,函数头部是对基类构造函数的调用,而不是声明,所以括号里的参数是实参,它们不但可以是派生类构造函数参数列表中的参数,还可以是局部变量、常量。比如上述程序中的Student::Student(char *name, int age, float score) : Teacher(name, age), score(score) {}
中Teacher(name,age)就是在头部调用的基类的构造函数,name,age是传入的实参。
构造函数的调用顺序
通过上面的程序可以发现,程序总是先调用基类的构造函数,再调用派生类的构造函数,若是多级继承(上述第一个程序),那么系统在创建C类对象时,构造函数会隐式的先调用A()再调用B()最后再调用C(),此时的A()代表的是A的构造函数,下同。这是按照自顶向下的顺序调用构造函数。注意定义派生类构造函数的时候,只能直接调用它直接继承的基类的构造函数,不能调用间接基类的构造函数,也就是说在C的头部可以直接调用B(),但是不能调用A(). 这样做的目的是为了节省内存,避免多次创建A()。
基类构造函数调用规则
C++规定创建派生类对象时必须要调用基类的构造函数,在定义派生类构造函数时最好要指明基类构造函数,若没有指明,则调用的是基类默认的构造函数(无参构造函数),若基类中含有带参的构造函数,则必须在定义派生类构造函数时显示调用基类构造函数,否则会出问题。
#include <iostream>
using namespace std;
class A {
public:
A(int w);
private:
int width;
};
A::A(int w) : width(w) {}
class B : public A {
public:
B(int l, int w);
private:
int length;
};
//若不指明调用基类的A(w),则会报错
//若在基类A中定义了A(){}无参构造函数,那么B()可以定义为:
//B::B(int l,int w):length(l){}这是隐式调用了基类的A()
B::B(int l, int w) : length(l), A(w) {}
int main() {
//若没有23行的代码,则会报错
B b(1, 2);
return 0;
}
继承中的析构函数
在C++中基类的析构函数和构造函数都不能被派生类继承,和构造函数不同的是,在派生类头部不用显示的调用基类的析构函数,因为每个类只有一个析构函数,程序会自动调用。
析构函数和构造函数在执行顺序上也有区别,析构函数会先销毁派生类的对象,再销毁基类的对象,这点和构造函数的执行顺序相反。
#include <iostream>
using namespace std;
class A {
public:
A(int w);
A();
~A();
private:
int width;
};
A::A(int w) : width(w) { cout << "A structure" << endl; }
A::A() { cout << "A no parameters structure" << endl; }
A::~A() { cout << "A destructed" << endl; }
class B : public A {
public:
B(int l, int w);
~B();
private:
int length;
};
B::B(int l, int w) : length(l) { cout << "B structure" << endl; }
B::~B() { cout << "B destructed" << endl; }
int main() {
B b(1, 2);
return 0;
}
输出:
A no parameters structure //基类A()先执行
B structure//派生类B() 后执行
B destructed //派生类~B()先执行
A destructed //基类~A()后执行
多重继承
#include <iostream>
using namespace std;
class A {
public:
A(int w, int h);
~A();
int get_width();
int get_height();
private:
int width;
int height;
};
A::A(int w, int h) : width(w), height(h) { cout << "A constructed" << endl; }
A::~A() { cout << "A destructed" << endl; }
int A::get_width() { return this->width; }
int A::get_height() { return this->height; }
class B {
public:
B(int len, int ar);
~B();
int get_length();
int get_area();
private:
int length;
int area;
};
B::B(int len, int ar) : length(len), area(ar) { cout << "B constructed" << endl; }
B::~B() { cout << "B destructed" << endl; }
int B::get_length() { return this->length; }
int B::get_area() { return this->area; }
class C : public A, public B {
public:
C(int w, int h, int l, int a, int c);
~C();
int get_con();
void show();
private:
int con;
};
//派生类对象定义,故意将调用B和A的顺序弄反,结果还是根据继承时的顺序来执行基类的构造函数
C::C(int w, int h, int l, int a, int c) : B(l, a), A(w, h), con(c) {
cout << "C constructed" << endl;
}
C::~C() { cout << "C destructed" << endl; }
int C::get_con() { return this->con; }
void C::show() {
cout << get_width() << "," << get_height() << ","
<< get_length() << "," << get_area() << "," << get_con() << endl;
}
int main() {
C c(1, 2, 3, 4, 5);
c.show();
}
输出:
A constructed
B constructed
C constructed
1,2,3,4,5
C destructed
B destructed
A destructed
C++中多继承的形式是class X:public A,private B,protected C{};
指的是C按照public继承A,private继承B,protected继承C。关于这三种修饰符的区别上面已提过,这里不再赘述。
上述程序中,我们使用派生类C分别以public的形式继承了基类A,基类B,这是一种多继承的方式,这里相当于是声明了C的基类,和继承基类的顺序,所以定义派生类C构造函数的时候,调用基类的顺序并不能决定基类构造函数被调用的顺序,基类构造函数的优先调用取决于继承声明时的顺序,故A()比B()先调用。 派生类析构函数先于基类的析构函数被销毁,先创建的基类构造函数对象后销毁,这和单继承是一样的。
命名冲突
C++多继承中,由于派生类继承多个基类,难免会出现部分基类中有相同命名的成员函数,若要调用这相同命名的成员函数,那么就该在成员函数前使用类名::
的形式用以区分。下列程序中的基类A和B中就存在相同命名的成员函数show(),在派生类C中区分的方式为A::show();B::show();
#include <iostream>
using namespace std;
class A {
public:
A(int w, int h);
~A();
int get_width();
int get_height();
void show();
private:
int width;
int height;
};
A::A(int w, int h) : width(w), height(h) { cout << "A constructed" << endl; }
A::~A() { cout << "A destructed" << endl; }
int A::get_width() { return this->width; }
int A::get_height() { return this->height; }
void A::show() { cout << "this is A.show()" << endl; }
class B {
public:
B(int len, int ar);
~B();
int get_length();
int get_area();
void show();
private:
int length;
int area;
};
B::B(int len, int ar) : length(len), area(ar) { cout << "B constructed" << endl; }
B::~B() { cout << "B destructed" << endl; }
int B::get_length() { return this->length; }
int B::get_area() { return this->area; }
void B::show() { cout << "this is B.show()" << endl; }
class C : public A, public B {
public:
C(int w, int h, int l, int a, int c);
~C();
int get_con();
void show();
private:
int con;
};
//派生类对象定义,故意将调用B和A的顺序弄反,结果还是根据继承时的顺序来执行基类的构造函数
C::C(int w, int h, int l, int a, int c) : B(l, a), A(w, h), con(c) {
cout << "C constructed" << endl;
}
C::~C() { cout << "C destructed" << endl; }
int C::get_con() { return this->con; }
void C::show() {
cout << get_width() << "," << get_height() << ","
<< get_length() << "," << get_area() << "," << get_con() << endl;
A::show();
B::show();
}
int main() {
C c(1, 2, 3, 4, 5);
c.show();
}
输出:
A constructed
B constructed
C constructed
1,2,3,4,5
this is A.show()
this is B.show()
C destructed
B destructed
A destructed