C++继承
:通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员。我们称已存在的用来派生新类的类为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。
C++继承分为三类:
公有继承
基类的公共成员和受保护成员在派生类中访问属性还是不变,在派生类中,只有基类的私有成员在派生类中不可直接访问
即派生类的访问权限:public不变、private不可见、protected不变。
私有继承
基类的公用成员和保护成员都以私人身份出现在派生类中,派生类中的其他成员可以直接访问基类的公共和保护成员
即派生类的访问权限:public和protected变为private
保护继承
基类的公用成员和保护成员都以保护身份出现在派生类中。
即派生类访问权限:public变成protected、protected不变
举个例子:比如在之前引入联合体的学校信息管理系统,在学校信息分为职工信息和学生信息,两者信息有相同的部分,也有不同的部分,我们把相同的部分作为基类,把两者不同的部分作为两个不同的派生类,这样就可以满足两种需求。
class information
{
public:
information(char a, char b, int c)
{
name = a;
sex = b;
age = c;
}
protected:
char name;
char sex;
int age;
};
class student :public information
{
public:
student(char a, char b, int c, int d, char e) :information(a, b, c)
{
stu_number = d;
college = e;
}
protected:
int stu_number;
char college;
};
class teacher :public information
{
public:
teacher(char a, char b, int c, int d, char e,char f) :information(a, b, c)
{
tea_number = d;
rank = e;
college = f;
}
protected:
int tea_number;
char rank;
char college;
};
int main()
{
student stu1('w', '男', 21, 123456, '计科');
teacher tea1('w', '男', 21, 123456, '教授', '计科');
return 0;
}
virtual
virtual主要用在两个方面:虚函数与虚基类。
虚函数
虚拟函数就是为了对“如果你以一个基础类指针指向一个衍生类对象,那么通过该指针,你只能访问基础类定义的成员函数”这条规则反其道而行之的设计。
class information
{
public:
information()
{
}
information(char a, char b, int c)
{
name = a;
sex = b;
age = c;
}
void fun()
{
printf("is information");
}
protected:
char name;
char sex;
int age;
};
class student :public information
{
public:
student()
{
}
student(char a, char b, int c, int d, char e) :information(a, b, c)
{
stu_number = d;
college = e;
}
void fun()
{
printf("is student");
}
protected:
int stu_number;
char college;
};
int main()
{
information *p1 = new information;
information *p2 = new student;
p1->fun();
p2->fun();
return 0;
}
输出结果如下
两次函数调用都调用的是基类中的fun函数
在基类的fun函数前加上virtual输入结果为
即基类指针可以调用派生类的成员函数
注意:
1.在c++中的继承中,如果基类声明了一个函数为虚函数,那么在派生类中不用声明同名函数为虚函数(不需要加virtual)也可以实现该函数为虚函数
2.基类中的析构函数必须为虚函数,否则会出现对象释放错误。以上例说明,如果不将基类的析构函数声明为virtual,那么在调用delete p2;语句时将调用基类的析构函数,而不是应当调用的派生类的析构函数,从而出现对象释放错误的问题。
3.重写函数的特征标必须与基类函数一致,否则将覆盖基类函数;
4.重写不同于重载。我对重载的理解是:同一个类,内部的同名函数具有不同的参数列表称为重载;重写则是派生类对基类同名函数的“本地改造”,要求函数特征标完全相同。当然,返回值类型不一定相同。
5.虚函数调用的原理:编译器给每一个包括虚函数的对象添加了一个隐藏成员:指向虚函数表的指针。(敲黑板,后面会用)
虚基类
在c++中,派生类可以继承多个基类。如果这多个基类又是继承自同一个基类时,那么派生类是不是需要多次继承基类中的内容?由此引入虚基类
class mate
{
public:
mate()
{
}
virtual ~mate()
{
}
protected:
int a;
int b;
};
class mate1 :virtual public mate
{
public:
mate1()
{}
protected:
int c;
};
class mate2 :virtual public mate
{
public:
mate2()
{}
protected:
int d;
};
class mate3 :public mate1, public mate2
{
public:
mate3()
{}
protected:
int e;
};
int main()
{
cout << sizeof(mate) << endl;
cout << sizeof(mate1) << endl;
cout << sizeof(mate2) << endl;
cout << sizeof(mate3) << endl;
return 0;
}
程序的运行结果是什么呢?
现在问题来了mate的大小为什么是12呢?
上面说过虚函数在调用时编译器会增加一个指向虚函数表的指针,大小为4字节,加上两个整形指针变量一共是12个字节。
再看mate1和mate2,每个派生类增加了一个整形变量,但是大小却增加了8个字节。这是因为它们在虚继承自mate类后,添加了一个隐藏的成员——指向虚基类的指针,占4个字节。
这样mate3 = 12 + 8 + 8 + 4 = 32个字节。
explicit
explicit关键字只能用于修饰只有一个参数的类构造函数, 它的作用是表明该构造函数是显示的, 而非隐式的, 跟它相对应的另一个关键字是implicit, 意思是隐藏的,类构造函数默认情况下即声明为implicit(隐式).
explicit关键字的作用就是防止类构造函数的隐式自动转换.
举个例子,
class stu{
public:
explicit stu(int i)
{
a = i;
}
};
int main()
{
stu p1(10); //可以赋值
stu p2 = 10; //出现错误,存在类型的隐式转换 10被转换为一个临时的stu对象
return 0;
}
template
C++的模板设施提供了一种机制,它能够将类函数定义内部的类型和值参数化。这些参数作为占位符,以后会绑定到实际类型上。以数组为例:
template <class type>
class Array {
explicit Array(int size = DefaultArraySize);
Array(type *array, int array size);
Array(const Array &rhs);
virtual ~Array()
{
delete[] p1;
}
bool operator==(const Array&)const
{}
bool operator!=(const Array&)const
{}
private:
static const int DefaultArrayize = 12;
int _size;
type *p1;
};
关键字template引入模板,参数由一堆尖括号括起来,有一个参数type。关键字class表明这个参数代表一个类型。标识符type代表实际参数名。