类之间可以建立联系,这就使得类可以有某种关系

类之间的关系

has-A:包含关系,一个类使用另一个已经定义好的类的数据

uses-A:使用关系,友元或者对象参数传递

is-A:是的关系,这就是继承,具有传递性不具有对称性

继承是类之间定义的一种重要关系,一个B类继承A类,或称从类A派生类B,类A称为基类(父类),类B称为派生类(子类)

基类和派生类

类继承关系的语法形式

class 派生类名:基类名表
{
数据成员和成员函数声明
};

基类名表构成:访问控制 基类名1, 访问控制 基类名2,···, 访问控制 基类名n

访问控制 表示派生类对基类的继承方式,使用关键字:

  • public 公有继承
  • private 私有继承
  • protected 保护继承

访问控制

派生类对基类成员的使用,与继承访问控制和基类中成员性质有关

公有继承:基类的公有成员 -> 派生类的公有成员,基类的保护成员 -> 派生类的保护成员

私有继承:基类的公有成员和保护成员 -> 派生类的私有成员

保护继承:基类的公有成员和保护成员 -> 派生类的保护成员

不论种方式继承基类,派生类都不能直接使用基类的私有成员

重名成员

派生类定义了与基类同名的成员,在派生类中访问同名成员时屏蔽了基类的同名成员

在派生类中使用基类的同名成员,显式地使用类名限定符:

类名::成员

派生类中的静态成员

基类定义的静态成员,将被所有派生类共享

根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质

派生类中访问静态成员,用以下形式显式说明:

通过类:类名::成员

通过对象:对象名.成员

基类的初始化

建立一个类层次后,通常创建某个派生类的对象,包括使用基类的数据和函数

C++提供一种机制,在创建派生类对象时用指定参数调用基类的构造函数来初始化派生类继承基类的数据

派生类构造函数声明为

派生类构造函数(变元表):基类(变元表),对象成员1(变元表)···对象成员n(变元表);

构造函数执行顺序:基类 -> 对象成员 -> 派生类

继承的应用实例

#include <iostream>
#include <iomanip> using namespace std; class Point
{
friend ostream &operator<< (ostream &, const Point &);
public:
Point(int = 0, int = 0);//带默认参数的构造函数
void setPoint(int, int);//对点坐标数据赋值
int getX() const
{
return x;
}
int getY() const
{
return y;
}
protected:
int x, y;//Point类的数据成员
}; class Circle :public Point
{
friend ostream &operator<<(ostream &, const Circle &);//友员函数
public:
Circle(double r = 0.0, int x = 0, int y = 0);//构造函数
void setRadius(double);/*置半径*/
double getRadius() const;/*返回半径*/
double area() const;//返回面积
protected:
double radius;//数据成员,半径
}; class Cylinder :public Circle
{
friend ostream & operator<<(ostream &, const Cylinder &);//友员函数
public:
Cylinder(double h = 0.0, double r = 0.0, int x = 0, int y = 0);//构造函数
void setHeight(double);/*置高度值*/
double getHeight() const;/*返回高度值*/
double area() const;/*返回面积*/
double volume() const;/*返回体积*/
protected:
double height;//数据成员,高度
}; //Point 类的成员函数
//构造函数,调用成员函数对x,y作初始化
Point::Point(int a, int b)
{
setPoint(a, b);
}
//对数据成员置值
void Point::setPoint(int a, int b)
{
x = a;
y = b;
}
//重载插入算符,输出对象数据
ostream &operator<<(ostream &output, const Point &p)
{
output << '[' << p.x << "," << p.y << "]";
return output;
} //Circle 类的成员函数
//带初始化式构造函数,首先调用基类构造函数
Circle::Circle(double r, int a, int b):Point(a, b)
{
setRadius(r);
}
//对半径置值
void Circle::setRadius(double r)
{
radius = (r >= 0 ? r : 0);
}
// 返回半径值
double Circle::getRadius() const
{
return radius;
}
// 计算并返回面积值
double Circle::area() const
{
return 3.14159 * radius * radius;
}
// 输出圆心坐标和半径值
ostream & operator<< (ostream &output, const Circle &c)
{
output << "Center = " << '[' << c.x << "," << c.y << "]" << "; Radius = "
<< setiosflags(ios::fixed | ios::showpoint) << setprecision(2) << c.radius;
return output;
} //Cylinder 类的成员函数
//带初始化式构造函数,首先调用基类构造函数
Cylinder::Cylinder(double h, double r, int x, int y):Circle(r, x, y)
{
setHeight(h);
}
// 对高度置值
void Cylinder::setHeight(double h)
{
height = (h >= 0 ? h : 0);
}
// 返回高度值
double Cylinder::getHeight() const
{
return height;
}
// 计算并返回圆柱体的表面积
double Cylinder::area() const
{
return 2 * Circle::area() + 2 * 3.14159*radius*height;
}
// 计算并返回圆柱体的体积
double Cylinder::volume() const
{
return Circle::area()*height;
}
// 输出数据成员圆心坐标、半径和高度值
ostream &operator<< (ostream &output, const Cylinder &cy)
{
output << "Center = " << '[' << cy.x << "," << cy.y << "]" << "; Radius = "
<< setiosflags(ios::fixed | ios::showpoint) << setprecision(2) << cy.radius
<< "; Height = " << cy.height << endl;
return output;
} int main()
{
Point p(72, 115);//定义点对象并初始化
cout << "The initial location of p is " << p << endl; p.setPoint(10, 10);//置点的新数据值
cout << "\nThe new location of p is " << p << endl;//输出数据 Circle c(2.5, 37, 43);//定义圆对象并初始化
cout << "\nThe initial location and radius of c are\n" << c << "\nArea = " << c.area() << "\n"; //置圆的新数据值
c.setRadius(4.25);
c.setPoint(2, 2);
//输出圆心坐标和圆面积
cout << "\nThe new location and radius of c are\n" << c << "\nArea = " << c.area() << "\n"; Cylinder cyl(5.7, 2.5, 12, 23);//定义圆柱体对象并初始化
//输出圆柱体各数据和表面积,体积
cout << "\nThe initial location, radius ang height of cyl are\n" << cyl
<< "Area = " << cyl.area() << "\nVolume = " << cyl.volume() << '\n';
//置圆柱体的新数据值
cyl.setHeight(10);
cyl.setRadius(4.25);
cyl.setPoint(2, 2);
cout << "\nThe new location, radius ang height of cyl are\n" << cyl
<< "Area = " << cyl.area() << "\nVolume = " << cyl.volume() << "\n"; system("pause");
return 0;
}

多继承

一个类有多个直接基类的继承关系称为多继承

多继承声明语法

class 派生类名:访问控制 基类名1, 访问控制 基类名2,···,访问控制 基类名n
{
数据成员和成员函数声明
};

类C可以根据访问控制同时继承类A和类B的成员,并添加自己的成员

class C:public A, public B

多继承的派生类构造和访问

  • 多个基类的派生类构造函数可以用初始式调用基类构造函数初始化数据成员
  • 执行顺序与单继承构造函数情况类似。多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序
  • 一个派生类对象拥有多个直接或间接基类的成员。不同名成员访问不会出现二义性。如果不同的基类有同名成员,派生类对象访问时应该加以识别

    e.g.
class Base1
{
public:
Base1(int x)
{
value = x;
}
int getData() const
{
return value;
}
protected:
int value;
}; class Base2
{
public:
Base2(char c)
{
letter = c;
}
char getData() const
{
return letter;
}
protected:
char letter;
}; class Derived : public Base1, public Base2
{
friend ostream &operator<< (ostream &, const Derived &);
public:
Derived(int, char, double);
double getReal() const;
private:
double real;
};

虚基类

如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性

class B
{
public:
int b;
};
class B1:public B
{
private:
int b1;
};
class B2:public B
{
private:
int b2;
};
class C:public B1, public B2
{
public:
int f();
private:
int d;
};
C c;
c.B;// error
c.B::b;// error,从哪里继承的?
c.B1::b;// ok,从B1继承的
c.B2::b;// ok ,从B2继承的

建立 C 类的对象时,B 的构造函数将被调用两次:一次由B1调用,另一次由 B2 调用,以初始化 C 类的对象中所包含的两个 B 类的子对象



  • 如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性
  • 如果在多条继承路径上有一个公共的基类,那么在继承路径的某处汇合点,这个公共基类就会在派生类的对象中产生多个基类子对象
  • 要使这个公共基类在派生类中只产生一个子对象,必须对这个基类声明为虚继承,使这个基类成为虚基类
  • 虚继承声明使用关键字:virtual
class B
{
public:
int b;
};
class B1:virtual public B
{
private:
int b1;
};
class B2:virtual public B
{
private:
int b2;
};
class C:public B1, public B2
{
private:
float d;
};
C cc;
cc.b;// ok

由于类 C 的对象中只有一个 B 类子对象,名字 b 被约束到该子对象上,所以,当以不同路径使用名字 b 访问 B 类的子对象时,所访问的都是那个唯一的基类子对象。即cc.B1::bcc.B2::b引用是同一个基类 B 的子对象



面向对象中的继承指类之间的父子关系

  1. 子类拥有父类的所有成员变量和成员函数
  2. 子类就是一种特殊的父类
  3. 子类对象可以当作父类对象使用
  4. 子类可以拥有父类没有的方法和属性

C++中的继承方式(public、private、protected)会影响子类的对外访问属性

  • public继承:父类成员在子类中保持原有访问级别
  • private继承:父类成员在子类中变为private成员
  • protected继承:父类中public成员会变成protected,父类中protected成员仍然为protected,父类中private成员仍然为private

    注意:private成员在子类中依然存在,但是却无法访问到。

继承总结

  • 继承是面向对象程序设计实现软件重用的重要方法。程序员可以在已有基类的基础上定义新的派生类。
  • 单继承的派生类只有一个基类。多继承的派生类有多个基类。
  • 派生类对基类成员的访问由继承方式和成员性质决定。
  • 创建派生类对象时,先调用基类构造函数初始化派生类中的基类成员。调用析构函数的次序和调用构造函数的次序相反。
  • C++提供虚继承机制,防止类继承关系中成员访问的二义性。
  • 多继承提供了软件重用的强大功能,也增加了程序的复杂性

C++学习笔记-继承的更多相关文章

  1. Java学习笔记---继承和super的用法

    自从换了个视频教学,感觉比原来那个好多了,就是学校网速太渣,好多视频看一会卡半天,只能先看看已经下载的了. 不过也好,虽然不能从开始开始重新开,但是已经看过一次,在看一次也是好的,就当巩固学习了. 继 ...

  2. Java学习笔记--继承和多态(中)

    1.通过继承来开发超类(superclass) 2.使用super 关键词唤起超类的构造方法 3.在超类中覆盖方法 4.区分override和overload 5.在Object类中探索toStrin ...

  3. java学习笔记-继承中super关键字

    背景: 在java继承的概念中我们得知,被声明为私有的类成员对所属的类来说仍然是私有的.类之外的任何代码都不能访问,包括子类. super关键字的两种用法: 1.用于调用超类的构造函数: 2.用于访问 ...

  4. JS高级程序设计学习笔记——继承

    我们知道,在OO语言中,继承可分为接口继承和实现继承.而ECMAScript的函数没有签名,不能实现“接口继承”,只能通过原型链实现“实现继承”. 在学习了各种继承模式之后,简单总结一下各种继承模式的 ...

  5. Java学习笔记--继承和多态(下)

    1.通过继承来开发超类(superclass) 2.使用super 关键词唤起超类的构造方法 3.在超类中覆盖方法 4.区分override和overload 5.在Object类中探索toStrin ...

  6. Java学习笔记--继承和多态(上)

    1.通过继承来开发超类(superclass) 2.使用super 关键词唤起超类的构造方法 3.在超类中覆盖方法 4.区分override和overload 5.在Object类中探索toStrin ...

  7. JavaScript学习笔记----- 继承的实现及其原理

    按照自己在极客上学习的顺序整理了一下,参考了几位前辈的随笔,十分感谢:                       参见http://blog.yemou.net/article/query/info ...

  8. [Android学习笔记]继承自ViewGroup的控件的过程学习

    ViewGroup文档 http://developer.android.com/training/index.html 继承自ViewGroup需要重写onLayout方法用来为子View设定位置信 ...

  9. java学习笔记 --- 继承

    继承 (1)定义:把多个类中相同的成员给提取出来定义到一个独立的类中.然后让这多个类和该独立的类产生一个关系,    这多个类就具备了这些内容.这个关系叫继承.  (2)Java中如何表示继承呢?格式 ...

  10. Java学习笔记——继承、接口、多态

    浮点数的运算需要注意的问题: BigDecimal operand1 = new BigDecimal("1.0"); BigDecimal operand2 = new BigD ...

随机推荐

  1. JAVA访问Zabbix API

    Zabbix 一.Zabbix 概述 zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案.zabbix能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的 ...

  2. PHP基础之搭建WAMP环境

    访问 http://www.wampserver.com/en/ 点击 点击 点击 由于WAMP需要 Microsoft Visual C++运行库支持,请先到 这里 下载VC++2012运行库.官方 ...

  3. HGOI 20191030am 题解

    Problem A 腿部挂件 给出$n$个数的序列$a_i$,支持$T$次操作. 每次操作形如$x , l , r$,计算$\max_{i = l}^{r} (a_i \oplus x)$的值. 对于 ...

  4. Codeforces Round #564 (Div. 1)

    Codeforces Round #564 (Div. 1) A Nauuo and Cards 首先如果牌库中最后的牌是\(1,2,\cdots, k\),那么就模拟一下能不能每次打出第\(k+i\ ...

  5. 26.Python三目运算符(三元运算符)用法详解

    Python 可通过 if 语句来实现三目运算符的功能,因此可以近似地把这种 if 语句当成三目运算符.作为三目运算符的 if 语句的语法格式如下: True_statements if expres ...

  6. SWPUCTF2019 | 神奇的二维码

    拖到binwalk里面发现4个压缩包: 查找一下RAR的文件头,然后把它们提取出来: 第一个base64一下得到第二个压缩包的密码: 第二个压缩包可以,emmm,保存一下扩充一下自己的表情包库: 第三 ...

  7. Android学习_7/25

     常用控件 Android控件使用规律:先定义id,再指定宽度和高度,然后适当加入一些控件特有的属性 1.         TextView 在界面上显示一段文本 2.         Button ...

  8. [题解] [SCOI2010] 生成字符串

    题面 题解 考虑到直接求合法方案不好求, 我们转化为用总方案减去不合法方案 总方案就是\(\binom{n+m}{m}\), 即在\(n+m\)个位置中放\(n\)个数 我们将初始的空序列看做\((0 ...

  9. webpack搭建多页面系统(一):对webpack 构建工具的理解

    为什么使用webpack构建工具? 1.开发效率方面: 在一般的开发过程中,分发好任务后,每个人完成自己单独的页面,如果有的人开发完成之后,接手别人的任务,就有可能造成开发时候的冲突. 如果利用模块化 ...

  10. 小程序支持npm包