c++继承
继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行时间的效果。
当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。
一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:
class 类名: 访问修饰符 基类名
访问修饰符 既作用域:
如不写则默认为 private
如:
#include <iostream>
using namespace std;
// 基类
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 派生类
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
// 输出对象的面积
cout << "Total area: " << Rect.getArea() << endl;
return 0;
}
输出为:
Total area: 35
访问控制和继承
派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。
一个派生类继承了所有的基类方法,但下列情况除外:
基类的构造函数、析构函数和拷贝构造函数。
基类的重载运算符。
基类的友元函数。
继承类型
当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符 来指定的。
通常使用 public 继承。当使用不同类型的继承时,遵循以下规则:
公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
多次继承
多继承即一个子类可以有多个父类,它继承了多个父类的特性。C++ 类可以从多个类继承成员,语法如下:
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{
<派生类类体>
};
//访问修饰符继承方式是 public、protected 或 private 其中的一个,用来修饰每个基类,各个基类之间用逗号分隔
#include <iostream>
using namespace std;
// 基类 Shape
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 基类 PaintCost
class PaintCost
{
public:
int getCost(int area)
{
return area * 70;
}
};
// 派生类
class Rectangle: public Shape, public PaintCost
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// 输出对象的面积
cout << "Total area: " << Rect.getArea() << endl;
// 输出总花费
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0;
}
结果是:
Total area: 35
Total paint cost: $2450
C++重载
函数重载(方法重载)
重载声明是指一个与之前已经在该作用域内声明过的函数或方法
具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。
当您调用一个重载函数或重载运算符时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策。
在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。不能仅通过返回类型的不同来重载函数。
#include <iostream>
using namespace std;
class printData
{
public:
void print(int i) {
cout << "整数为: " << i << endl;
}
void print(double f) {
cout << "浮点数为: " << f << endl;
}
void print(char c[]) {
cout << "字符串为: " << c << endl;
}
};
int main(void)
{
printData pd;
// 输出整数
pd.print(5);
// 输出浮点数
pd.print(500.263);
// 输出字符串
char c[] = "Hello C++";
pd.print(c);
return 0;
}
输出为:
整数为: 5
浮点数为: 500.263
字符串为: Hello C++
运算符重载
运算符重载是少数C++的特色重载,这在java或者python中是没有的,通过运算符重载,我们可以重定义大部分C++的内置运算符。
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表,比如说两个Box对象的相加+,就可以这么重载+号:
Box operator+(const Box&);
这样的话:
Box b1,b2,b3;
b1 = b2+b3;
//就可以这样使用,这里重载了+号运算符
#include <iostream>
using namespace std;
class Box
{
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{
breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
// 程序的主函数
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 把体积存储在该变量中
// Box1 详述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 详述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// Box1 的体积
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// Box2 的体积
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// 把两个对象相加,得到 Box3
Box3 = Box1 + Box2;
// Box3 的体积
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
输出:
Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400
可重载运算符
不可重载运算符
多态
多态的基本概念
多态性是一个接口多种实现,分为类的多态性和函数多态性。
函数的多态性(重载)是指一个函数被定义成多个不同参数的函数。
在基类的函数前加上virtual关键字(即虚函数),在派生类中重写该函数,
运行时将会根据对象的实际类型来调用相应的函数。如果对象类型
是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数
#include<iostream>
using namespace std;
float Sum(float a, float b)
{
return a + b;
}
int Sum(int a, int b)
{
return a + b;
}
int main()
{
int var1, var2;
float var3, var4;
cin >> var1 >> var2;
cin >> var3 >> var4;
cout << Sum(var1, var2) << endl << Sum(var3, var4) << endl;
system("pause");
return 0;
}
结果:
从程序代码中可以看出,定义了两个同名的函数Sum,他们的不同之处在于函数参数类型以及返回值类型的不同。
在对程序进行编译时,编译器并没有报错,并且程序的运行结果也是我们所期待的。因为在此程序中使用了函数
多态(重载)这一特性,这样编译器在对代码进行编译的过程中就会通过函数参数的不同来进行同名函数的选择调用,
选择出最合适的函数类型。这就是函数多态的方便之处
类的多态
#include<iostream>
using namespace std;
class Mammal {
public:
void speak() {
cout << " Mammal::speak " << endl;
}
};
class Dog :public Mammal {
public:
void speak()
{
cout << " Dog::speak " << endl;
}
};
int main()
{
Dog a;
a.speak();
Mammal *b = &a;
b->speak();
system("pause");
return 0;
}
运行结果:
以上代码中,定义了一个基类Mammal,在由其派生出了类Dog,二者都有函数speak(),我们
在main函数里面首先调用了Dog类的speak函数,然后通过将Dog类的a的地址赋给Mammal类b,
我们并想以这种方式来通过基类来调用派生类的函数,但是从程序运行的结果来看,这种方式
并不可取。
那若想得到我们所期待的结果,此时就需要用到类的多态性。我们只需将
基类中的speak函数声明为虚函数,即加上
virtual
关键字:
class Mammal {
public:
virtual void speak() {
cout << " Mammal::speak " << endl;
}
};
此时再将程序运行:
这样就得到了我们所想要的结果。
在类的多态性里面还有纯虚函数,含有纯虚函数的类也称之为抽象类:
纯虚函数的使用:
1,当想在基类中抽象出一个方法,且该基类只做能被继承,而不能被实例化;
2,这个方法必须在派生类(derived class)中被实现;
构造纯虚函数只需要在虚函数定义是在右边加上
“= 0”
的格式就可以。
下面是利用类的继承来计算矩形和圆的面积与周长的程序(纯虚函数):
#include<iostream>
using namespace std;
#define PI 3.1415926
class Shape {
public:
virtual double getArea() = 0;
virtual double getPerim() = 0;
};
class Rectangle :public Shape {
public:
virtual double getArea();
virtual double getPerim();
Rectangle(double a = 1, double b = 1) :r1(a), r2(b) {
}
private:
double r1;
double r2;
};
double Rectangle::getArea() {
return r1 * r2;
}
double Rectangle::getPerim() {
return 2 * (r1 + r2);
}
class Circle : public Shape {
public:
virtual double getArea();
virtual double getPerim();
Circle(double a) :r(a) {
}
private:
double r;
};
double Circle::getArea()
{
return PI * r*r;
}
double Circle::getPerim()
{
return 2 * PI*r;
}
int main()
{
Rectangle a(1.2, 3.4);
Circle b(3);
cout << "Rectangle的面积:" << a.getArea() << endl;
cout << "Rectangle的周长:" << a.getPerim() << endl;
cout << "Circle的面积:" << b.getArea() << endl;
cout << "Circle的周长:" << b.getPerim() << endl;
system("pause");
return 0;
}
因为矩形与圆的周长与面积的计算方式不同,所以在这里可以将基类定义为抽象类。在此
程序中定义了一个抽象类Shape,在此基础上派生出类Retangle和Circle。这两个派生类
都继承了抽象类里面的纯虚函数getArea(),getPerim()二者分别为计算面积以及周长的函数。
因为在派生类中纯虚函数不能被直接继承,所以在两个类中,纯虚函数都被重新定义。
下面是程序的运行结果:
严格来说python是没有多态的。
值得注意的是,python的多态和C++的多态是不一样的。在C++中,多态的目的则是为了接口重用。也就是说,不论传递过来的究竟是那个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。也就是说,C++会对传入的参数类型等进行判断来确定使用子类还是父类的方法。python只有重写,并不能对传入的参数类型进行判断。
在这个程序中纯虚函数的优越性就直接体现出来了。