一、继承
继承是c++中的一种机制,是面向对象复用的重要手段。通过继承机制,可以利用已有的类来定义新的类,新的类不仅拥有新的成员,同时也拥有旧的成员。把已存在的类称为父类或基类,新定义的类称为子类或派生类,继承是类之间的关系建模。
class Person{ //Person是父类
public:
Person(const string& name)
: _name(name)
{}
void show{
cout<<name<<endl;
}
protected:
string _name;
};
class Student:public Person{ //Student是子类,public是继承关系
protected:
int _num;
}
继承方式有三种:
1.公有继承(public)
#include<iostream>
#include<string>
using namespace std;
class Student
{
public :
Student(string n, int g,int a)
{
name = n;
grade = g;
age = a;
}
void show()
{
cout << "Student:" << endl;
cout << "name=" << name << endl;
cout << "grade=" << grade<<endl;
cout << "age=" << age << endl;
}
protected:
string name;
int grade;
private:
int age;
};
class GraduateStudent :public Student
{
public:
GraduateStudent(string n, int g, int a) :Student(n, g, a) //调用基类的构造函数,构造基类
{
cout << "GraduateStudent" << endl;
}
void show1()
{
cout << "GraduateStudent:" << endl;
cout << "name= " << name << endl;
cout << "grade= " << grade << endl;
}
};
void main()
{
GraduateStudent g("xiaoming", 9, 15);
g.show(); //子类可以直接访问基类的公共成员
g.show1();
}
公有继承会把父类的公有成员继承到子类的公有成员,保护成员会变成子类的保护成员,私有成员子类不可以访问。
结论:
父类的公有成员可继承为子类的公有成员,在子类和外部都可以访问;
父类的受保护成员继承为子类的受保护成员,只有在子类可访问;
父类的私有成员,子类不可以访问。
2.保护继承(protected)
class Student
{
public :
Student(string n, int g,int a)
{
name = n;
grade = g;
age = a;
}
void show()
{
cout << "Student:" << endl;
cout << "name=" << name << endl;
cout << "grade=" << grade<<endl;
cout << "age=" << age << endl;
}
string name; //公有
int grade;
private:
int age;
};
class GraduateStudent :protected Student
{
public:
GraduateStudent(string n, int g, int a) :Student(n, g, a) //调用基类的构造函数,构造基类
{
cout << "GraduateStudent" << endl;
}
void show1()
{
cout << "GraduateStudent:" << endl;
cout << "name= " << name << endl;
cout << "grade= " << grade << endl;
}
};
void main()
{
GraduateStudent g("xiaoming", 9, 15);
g.show(); //出错,无法直接访问子类的公有成员
g.print1();
}
保护继承会把父类的公有成员或保护成员都变成子类的保护成员,但对于私有成员,子类仍不可以访问。
结论:
父类的公有成员,子类继承为保护成员,只能在子类访问;
父类的保护成员,子类继承为保护成员,只能在子类访问;
父类的私有成员,子类不可以访问。
3.私有继承(private)
父类的公有成员,子类继承为私有成员,只能在子类访问;
父类的保护成员,子类继承为私有成员,只能在子类访问;
父类的私有成员,子类不可以访问。
不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,但是基类的私有成员在子类中不能访问。
public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每个子类对象也都是一个父类对象。
protetced/private继承是一个实现继承,基类的部分成员并未完全成为子类接口的一部分,是 has-a 的关系原则,在非特殊情况下不会使用这两种继承关系,绝大多数的场景下使用的都是公有继承。
二、派生类的默认成员函数
类的默认成员函数包括:拷贝构造函数、构造函数、析构函数、赋值操作符重载、取地址操作符重载、const修饰的取地址操作符重载。
在继承关系里面,如果派生类没有显示定义这六个函数,那么编译系统会自动合成这六个默认成员函数。
三、隐藏(重定义)
隐藏是指子类隐藏了父类的函数,特点是:
1)子类函数与父类函数的名称相同,参数不同,则父类函数被隐藏,此时的父类函数有无virtual修饰均可构成隐藏;
2)子类函数与父类函数的名称、参数相同,但父类函数没有virtual,则父类函数被隐藏(若有virtual修饰,是构成覆盖)。
与隐藏定义相似的是覆盖(重写),覆盖是指子类中的函数,其函数名、参数列表、返回值类型都与父类中被类中被覆盖的函数相同,且
父类中被覆盖的函数必须有virtual修饰。
覆盖的特点为:
1)子类函数与父类函数不在同一作用域,分别位于子类和父类;
2)函数名、参数类型相同;
3)父类中被覆盖的函数是虚函数。
用一代码说明:
class father
{
public:
void show1()
{
cout << "father::show1" << endl;
}
virtual void show2()
{
cout << "father::show2" << endl;
}
};
class son:public father
{
public:
void show1()
{
cout << "son::show1" << endl;
}
void show2()
{
cout << "son::show2" << endl;
}
void show2(string s)
{
cout << "son::void show2(string s)" << endl;
}
};
int main()
{
son s;
s.show1();
s.show2();
s.show2("aaa");
return 0;
}
主函数中第一句执行语句是隐藏(函数名、参数相同,父类函数没有virtual);第二句是覆盖(函数名、参数相同,且有virtual修饰父类函数);第三句是隐藏(函数名相同,参数不同)。
四、菱形继承
如上图,B、C继承A,D继承B、C,这个关系就是菱形继承。
class A
{
protected:
int _a;
public:
void show()
{
cout << "A::show" << endl;
}
};
class B:public A
{
protected:
int _b;
};
class C : public A
{
protected:
int _c;
};
class D :public B, public C
{
private:
int _d;
};
int main()
{
D d;
d.show();//报错,调用不明确
}
D的对象模型中保存了两份A,当调用A中的函数show时,就会出现调用不明确的情况,不仅是数据冗余而且还浪费空间。这就是菱形继承中经常出现的问题:数据冗余和二义性。c++中通常使用虚继承来解决这两个问题。
虚继承:
如图,虚继承就是在B、C在继承A时,在它们前面加上virtual关键字。
使用虚继承之后,B、C中就不是保存A的内容,而是保存了一份偏移量地址,并且A是被放在了一块公共的空间,这样做就降低了数据冗余性。

5856

被折叠的 条评论
为什么被折叠?



