目录
1. 继承是什么
继承(inheritance)机制是面向对象程序设计中使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生的新类,称派生类(或子类),被继承的类称基类(或父类)。
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。之前接触的复用都是函数复用,继承是类设计层次的复用。
2.继承的用途
如果我们要写许多的水果类,如苹果,橘子等。但我们发现它们有一些共同特征,即都有名字,大小,等,我们可以抽象出一个水果类,对于每一个具体的水果,继承水果类即可。大大减少了冗余成分。
3.继承的语法
在类名的后面加上以上方式即可构成继承,即B继承了A中的内容,public表示以公有形式继承A中的内容。
例如:
#include<iostream>
using namespace std;
class A
{
public:
void print()
{
cout << "A" << endl;
}
};
class B :public A
{
};
int mainn()
{
B t;
t.print();
return 0;
}
这里我们可以发现,B中没有print函数,都是我们用B实例化出的对象却能够调用A中的print函数,这是因为我们使用了继承。其中A叫做父类(基类),B叫做子类(派生类)。
4.继承的权限问题
我们这里用一个公式:public>protect>private。
即最后子类继承父类的内容的访问权限取父类成员的访问权限与继承方式的最小值(按照上式比较)。
注意:父类中私有成员虽然无论子类以什么方式继承,子类都不能访问,但是子类中仍然继承了父类的私有成员。
5.关于父类中构造函数与析构函数
#include<iostream>
using namespace std;
class A
{
public:
A()
{
cout << "A" << endl;
}
~A()
{
cout << "~A" << endl;
}
};
class B :public A
{
public:
B()
{
cout << "B" << endl;
}
~B()
{
cout << "~B" << endl;
}
};
int main()
{
B t;
return 0;
}
实际上,我们在调用子类构造函数时编译器会先在初始化列表调用父类的构造函数,但是调用子类的析构函数时,会等子类的析构函数调用完后再调用父类的析构函数。
如果想要显示调用父类构造函数,直接把父类名当函数名即可。
子类不能直接显示调用父类的析构函数(后续会讲解)。
另外:
1.派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
2.派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
3.派生类的 operator= 必须要调用基类的 operator= 完成基类的复制。
6.关于用子类对象初始化父类对象
#include<iostream>
using namespace std;
class A
{
public:
A(int x=1,int y=2)
:a(x)
,b(y)
{}
void print()
{
cout << a << " " << b << " " << endl;
}
int a;
int b;
};
class B :public A
{
public:
B(int z=3)
:t(z)
,A(6,6)
{}
private:
int t;
};
int main()
{
B m(8);
m.print();
A n = m;
n.print();
return 0;
}
这里我们用子类对象初始化父类对象,会发现子类对象中有关父类的内容给了父类对象。
实际上这里父类对象会对子类对象的内容进行切割,只保留与自己有关的内容。
对于父类类型的指针,它指向的是有关父类的部分,父类类型的引用以此类推。
注意:不能用父类对象初始化子类对象。
7.关于子类与父类中同名成员的问题
7.1 同名成员
#include<iostream>
using namespace std;
class A
{
public:
A(int x=1,int y=2)
:a(x)
,b(y)
{}
int a;
int b;
};
class B :public A
{
public:
B(int z=3)
:a(z)
{}
void print()
{
cout << a << " " << b << " " << endl;
}
private:
int a;
};
int main()
{
B m;
m.print();
return 0;
}
这里我们子类对象与父类对象都有成员变量a,但优先访问的是子类中的a。
如果想要访问父类对象中的a,指明作用域即可。
7.2 同名函数
#include<iostream>
using namespace std;
class A
{
public:
void print()
{
cout << "A" << endl;
}
};
class B :public A
{
public:
void print()
{
cout << "B" << endl;
}
};
int main()
{
B m;
m.print();
return 0;
}
这里会优先调用子类的函数。同样,想调用父类的函数指明作用域即可。
但是如果我们在子类中print函数加上参数
会发现报错了,这里不会根据哪个函数更适合就调用哪一个,实际上当子类出现与父类同名的函数时,会进行覆盖,无论调用时是否传参,只要是子类对象调用,就只会调用子类的函数,除非调用时指明作用域。
8.单继承与多继承
只继承一个父类对象叫单继承,继承多个父类对象叫多继承。
多继承的语法如下:
用逗号隔开多个继承的对象。
这里需要注意,如果两个子类继承同一个父类,再由另一个子类继承这两个子类,这种方式叫做棱形继承,这里会出现多个同名成员问题,使用是就需要注意作用域的区分,否则编译器会不知道调用哪一个。
如上例报错。