继承和派生
class Student:public Person
派生类把基类全部的成员(不包括构造函数和解析函数)继承下来
公有继承
基类成员继承给派生类后,其访问权限不变,但基类成员的私有成员在派生类中不可以直接访问
私有继承
基类中的所有成员都以私有成员身份出现在派生类中,基类的私有成员在派生类中不可直接访问
保护成员和保护继承
从用户的角度来看,保护成员等价于私有成员,但是有一点不同,保护成员可以被派生类的成员函数使用
在保护继承中,基类的公有和保护成员都以保护成员的身份出现在派生类中,而基类的私有成员不可以直接访问
派生类的构造函数
class Person
{
public:
Person()
{
id = 1;
name = "";
sex = 'L';
}
Person(int id,string name,char sex)
{
this->id = id;
this->name = name;
this->sex = sex;
}
private:
int id;
string name;
char sex;
};
class Student:public Person
{
// 在派生类构造函数的形参列表中,要给出初始化基类和派生类的数据成员所需要的全部参数
Student(int id,string name,char sex,float score):Person(id,name,sex)
{
this->score = score;
}
private
float score;
};
当创建一个派生类的对象时,系统首先自动调用基类构造函数,再调用派生类的构造函数。
如果一个类只作为基类使用,一般不会用于实例化对象,就会把这个基数的数据成员和构造函数设置为protected访问权限,这样既保证了数据的封装性,又能在派生类中访问,保证了数据的一致性。
派生类的析构函数
先执行派生类的析构函数,然后调用基类的析构函数。
多继承
#include<iostream>
using namespace std;
class A
{
public:
A(int x)
{
this->x = x;
}
private:
int x;
};
class B
{
public:
B(int y)
{
this->y = y;
}
private:
int y;
};
class C:public A,public B
{
public:
C(int x,int y,int z):A(x),B(y)
{
this->z = z;
}
private:
int z;
}
同名函数的屏蔽:
如果派生类和基类的函数同名,但是参数不同,此时,无论有没有virtual关键字,基类函数被隐藏。
如果函数同名,参数相同,并且无virtual关键字,基类函数被隐藏。(注意和函数重写的区别)
采用:“对象名.基类名::成员名”唯一访问函数
多继承的时候可能会产生二义性问题。c++提供虚基类的方法,当基类通过多个路径被一个派生类继承时,该派生类只继承该基类一次。
class A:virtual public M
类的组合
继承时纵向的,组合是横向的。
#include<iostream>
using namespace std;
class Point
{
public:
Point():x(0),y(0)
{
cout<<"Point's default constructor was called"<<endl;
}
Point(double x,double y):x(x),y(y)
{
cout<<"Point's constructor was called"<<endl;
}
Point(Point &p)
{
x = p.x;
y = p.y;
cout<<"Point's copyConstructor was called"<<endl;
}
private:
double x;
double y;
};
class Line
{
public:
Line():A(),B()
{}
Line(double x1,double y1,double x2,double y2):A(x1,y1),B(x2,y2)
{}
Line(Point p1,Point p2):A(p1),B(p2)
{}
Private:
Point A;
Point B;
};
基类和派生类的转换
不能把一个整形数据赋值给指针变量
可以用派生类对象对基类对象进行赋值,并且具有赋值兼容性(公有继承)
多态性与虚函数
虚函数
满足的条件:
- 基类与子类之间为公有继承方式
- 基类函数要声明为虚函数
- 通过基类指针或者引用来调用函数
class A
{
public:
virtual void display
{}
};
class B:public A
{
public:
void display
{}
}
void main()
{
A *pa;
B ba;
pa = &ba;
pa->display();
// 虽然pa是A类的对象,但是执行的是B类的display()
}
在运行过程中,根据基类指针指向不同的派生类对象就可以访问响应对象的虚函数,从而实现了运行时的动态多态性。派生类的虚函数重写或者覆盖了基类的同名虚函数。
注意
- virtual关键字只能出现在虚函数的原型声明中,在虚函数的实现中不能出现。
- 将基类中的某一个成员声明为虚函数后,派生类中的同名函数(函数名相同,参数列表完全一样,返回值类型相同)自动成为虚函数。
- 如果声明了某个成员函数为虚函数,则在该类中不能出现同名函数(函数名相同,参数列表完全一样,返回值类型相同),在以该类为基类的派生类中,也不能出现同名函数。
- 非类的成员函数不能定义为虚函数,类的成员函数中静态成员函数和构造函数不能定义为虚函数,当可以将析构函数定义为虚函数。
- 声明虚函数的时候,必须实现函数体。
- 当通过基类指针删除指向子类的动态对象时,子类的析构函数没有被执行,说明子类对象动态分配的空间没有得到释放,造成了内存泄露。为解决这个问题,常常将基类的析构函数定义为虚函数。
纯虚函数
virtual void Area(); //错误,必须在基类中实现
virtual void Area()
{
}
//正确
virtual void Area() = 0;
//纯虚函数
抽象类
不用实例化对象,只作为一种“模板”用于派生的类,成为抽象类。
运算符重载
A operate+ (A &a1,A &a2)
{
int x = a1.x + a2.x;
A a(x);
return a;
}
//定义一种新的加法算法
一般来说,重载函数中要用到一些private的变量,所以一般把重载函数定义为友元函数。
friend A operate+ (A &a1,A &a2);
有两个实例!!