本文主要是介绍C++ 第8章 继承,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
继承(Inheritance)是面向对象程序设计中软件重用的关键技术。
8.1 类之间的关系
一个大的应用程序,通常由多个类构成,类与类之间互相协同工作。
class Vehicle
{int wheels;double weight;double loading;
public:void initialize(int in_wheels, double in_weight);int get_wheels();double get_weight();double get_loading();
};class Car:public Vehicle
{int passenger_load;public initialize(int in_wheels, doublie in_weight, int people=4);int passengers();
}
前驱结点称为基类(或父类,超类)
后断结点称为派生类(或子类)
8.2 基类和派生类
C++中,描述类继承关系的语句格式:
class 派生类名 : 基类名表
{
数据成员和成员函数说明
};
其中,“基类名表”的语句格式:
访问控制 基类名1, 访问控制 基类名2,…,访问控制 基类名。
“访问控制”是表示继承权限的关键字,称为访问描述符。可以是:
public 公有继承
private 私有继承
protected 保护继承
如果省略访问描述符,则C++认为是私有继承。
8.2.1 访问控制
一个派生类的成员由两部分组成,一部分从基类继承过来,另一部分自已定义,即创建一个派生类对象时,系统会建立所有继承的和自身定义的成员。
一个派生类公有继承一个基类时,基类中所有公有成员(由public定义的数据成员或成员函数)成为派生类的公有(public)成员,基类中所有保护成员(由protected定义的数据成员或成员函数)成为派生类的保护(protected)成员。
一个派生类私有继承一个基类时,基类中所有公有成员和保护成员同时成为派生类的私有(private)成员。
一个派生类保护继承一个基类时,基类中所有公有成员和保护成员同时为派生类的保护(protected)成员。
1.公有继承
#include <iostream>
#include <cstring>
#include <cmath>using namespace std;class A
{public:void get_XY(){cout<<"Enter two number of x,y:";cin>>x>>y;}void put_XY(){cout<<"x = "<<x<<",y = "<<y<<"\n";}protected:int x,y;
};class B:public A
{public:int get_S(){ return s; }void make_S(){ s = x * y; } //使用基类数据成员x,yprotected:int s;
};class C:public B
{public:void get_H(){cout <<"Enter a number of h:";cin >> h;} int get_V(){return v;}void make_V(){make_S(); //使用基类成员函数v = get_S()*h; //使用基类成员函数}protected:int h,v;
};int main()
{A objA;B objB;C objC;cout<<"It is object_A:\n";objA.get_XY();objA.put_XY();cout <<"It is object_B:\n";objB.get_XY();objB.make_S();cout<<"S = "<<objB.get_S()<<endl;cout<<"It is object_C:\n";objC.get_XY();objC.get_H();objC.make_V();cout<<"v = "<<objC.get_V()<<endl;
}
在派生类中,通过this指针调用基类的成员函数
void make_V()
{
make_S(); //this->make_S()
v = get_S() * h; //this->get_S()
}
测试派生类对象继承基类的私有数据成员:
#include <iostream>
using namespace std;
class A
{public:A() { x = 1; }int out() { return x; }void addX() { x++ ;}private:int x;
};class B : public A
{public:B() // B类构造函数{ y = 1; }int out() // B类成员函数{ return y; } //返回this->y的值void addY(){ y++; }private:int y;
};int main()
{A a;cout<<"Structed a:\n";cout<<"a.x = "<<a.out()<<endl;B b;cout<<"Structed b:\n";cout<<"b.x = "<<b.A::out()<<endl; //输出b.xcout<<"b.y = "<<b.out()<<endl; //输出b.ycout<<"Object b data + 1:\n";b.addX(); //b.x++b.addY(); //b.y++cout<<"b.x="<<b.A::out()<<endl; //输出b.xcout<<"b.y="<<b.out()<<endl; //输出b.y
}
2.私有继承
以私有方式继承的派生类,基类的public和protected成员会成为派生类的私有成员,即基类中定义的public和protected成员只能在私有继承的派生类中可见,而不能在类外使用。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>using namespace std;class A
{public:void get_XY(){cout<<"Enter two numbers of x and y:";cin>>x>>y;};void put_XY(){cout<<"x = "<<x<<",y = "<<y<<endl;};protected:int x,y;
};class B :private A
{public:int get_S(){ return s;};void make_S(){get_XY(); //调用基类成员函数s = x * y;};private:int s;
};int main()
{B objB;cout<<"It is object_B:\n";objB.make_S();cout<<"S = "<<objB.get_S()<<endl;
}
3.保护继承
保护继承把基类的公有成员和保护成员作为派生类的保护成员,使其在派生类中被屏蔽。保护继承和私有继承方式在程序设计中应用较少。
4.访问声明
C++提供一种访问调节机制,使一些本来在派生类中不可见的成员变为可访问的,称为访问声明。
class B:private A
{ public:A::get_XY; //声明继承的成员函数名int get_S(){return s;};void make_S(){ s = x * y;};private:int s;
};int main()
{B objB;objB.get_XY(); //调用调整后的成员函数objB.make_S();cout<<"S = "<<objB.get_S()<<endl;
}
访问声明的格式:
基类名::成员
注意以下事项:
(1)访问声明仅调整名字的访问权限;
当被声明对象是数据成员时,不可说明为任何类型;当被声明对象为成员函数时,只能是函数名本身,不能带参数和返回类型说明:
class B
{public:int c;//...
};
class D:private B
{int d;public :int B::c //错误,带类型
}
(2)访问声明不允许在派生类中降低或提升基类成员的可访问性
class B
{public: int a;private: int b;protect: int c;
};
class D:private B
{public:B::a; //正确B::b; //错误,私有成员不能用于访问声明protectd:B::c; //正确B::a; //错误,不能降低基类成员的可访问性
};
(3)对重载函数名的访问声明将调整基类所有同名函数的访问域
a.调整同名的重载函数
class X
{pubic:f();f(int);
};class Y:private X
{public:X::f; //使X::f()和X::f(int)在Y中都为公有的
};
b.不同访问域的重载函数名不能用于访问声明
class X
{private:f(int);public:f();
};
class Y:private X
{X::f; //错误,访问声明具有二义性,不能调整其访问性
};
c.派生类中与基类名字相同的成员不可调整访问权限
class X
{public:f();
};
class Y:private X
{public:void f(int);X::f(); //错误,f有两次说明,不能调整其访问权限
};
8.2.2 重名成员
C++允许派生类的成员与基类成员重名。在派生类中访问重名成员时,屏蔽基类的同名成员。如果要在派生类中使用基类的同名成员,可以显式地使用作用域符指定:
类名::成员
1.重名数据成员
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>using namespace std;class Base
{public:int a, b;
};class Derived :public Base
{public:int b,c;
};int main()
{Derived d;d.a = 1;d.Base::b = 2; //Base::b 使用的是Base类的数据成员bd.b = 3; //这里使用的是Derived类的数据成员bd.c = 4;cout<<"d.a="<<d.a<<"\td.Base::b="<<d.Base::b<<"\td.b="<<d.b<<"\td.c"<<d.c<<endl;
}
2.重名成员函数
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>using namespace std;
class A
{public:int a1, a2;A(int i1=0, int i2 = 0){ a1 = i1; a2 = i2;}void print(){cout<<"a1="<<a1<<"\ta2="<<a2<<endl;}
};
class B:public A
{public:int b1, b2;B(int j1=1, int j2=2){ b1 = j1; b2 = j2;}void print() //定义同名函数{cout<<"b1="<<b1<<"\tb2="<<b2<<endl;}void printAB(){A::print(); //派生类对象调用基类版本同名成员函数print(); //派生类对象调用自身的成员函数}
};int main()
{B b;b.A::print();b.print();b.printAB();
}
8.2.3 派生类中访问静态成员
如果在基类中定义了静态成员,这些静态成员将在整个类体系中被共享,根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质。
在派生类中访问静态成员:
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>using namespace std;class B
{public:static void Add(){i++;}static int i;void out(){cout<<"static i="<<i<<endl;}
};int B::i=0;class D:private B
{public:void f();
};void D::f()
{i = 5; //i是类D的私有静态数据成员,在类中可见Add(); //Add()是类D的私有静态成员函数,在类中可见B::i++;B::Add();
}int main()
{B x;D y;x.Add();x.out();y.f();cout<<"static i="<<B::i<<endl; //正确,i是类B的公有静态数据成员cout<<"static i="<<x.i<<endl; //正确,i是类B的公有静态数据成员//cout<<"static i="<<y.i<<endl; //错误,i是类D的私有静太数据成员
}
8.3 基类的初始化
在派生类创建对象时,能够通过派生类的构造函数将指定参数传递给基类的带参构造函数。派生类的构造函数使用冒号语法的参数初始式实现这种功能:
构造函数名(变元表):基类(变元表),数据成员1(变元表),…,数据成员n(变元表)
类继承关系中构造函数的执行顺序:
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cstring>using namespace std;class Base
{public:Base(){cout<<"Base Created.\n";};
};class D_class:public Base
{public:D_class(){cout<<"D_class Created.\n";};
};int main()
{D_class d;
}//运行结果
Base Created. //首先,执行基类构造函数
D_class Created. //其次,执行派生类构造函数
对基类数据成员进行初始化:
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cstring>using namespace std;class Base
{public:int x;Base(int i):x(i){}
};class Derived:public Base
{int a;public:Derived(int j):a(j*5),Base(2){cout<<"Base::x="<<x<<"\tDerived::a="<<a<<endl;}
};int main()
{Derived d(3);
}//运行结果
Base::x=2 Derived::a=15
8.4 继承的应用实例
8.5 多继承
一个派生类只有一个直接基类,称为“单继承”。一个类可以从多个基类派生出来,即一个类有多个直接基类,称为“多继承”。
多继承说明只需在派生类名的冒号”:”之后跟上用逗号分隔的基类名列表即可。
class B1
{//...
};
class B2
{//...
};class C:public B1, public B2
{//...
};
8.5.1 多继承的派生类构造和访问
多个基类的派生类构造函数可以通过继承路径调用基类构造函数,执行顺序与单继承构造函数情况类似,多个直接基类,由派生类时指定的顺序执行构造函数。
Base1.h
#ifndef BASE1_H
#define BASE1_H
class Base1
{public:Base1(int x){value = x;}int getData()const{return value;}protected:int value;
};
#endif//Base2.h
#ifndef BASE2_H
#define BASE2_H
class Base2
{public:Base2(char c){letter = c;}char getData()const{return letter;}protected:char letter;
};
#endif//Derived.h
#ifndef DERIVED_H
#define DERIVED_H
class Derived:public Base1, public Base2 //公有继承Base1,Base2
{friend ostream &operator <<(ostream &, const Derived &);public:Derived(int , char, double);double getReal()const;private:double real;
}//派生类的构造函数
Derived::Derived(int i, char c, double f):Base1(i), Base2(c),real(f){}double Derived::getReal()const
{return real;
}ostream &operator<<(ostream &output, const Derived & d)
{output<<" Interger:"<<d.value<<"\n Character:"<<d.letter<<"\n Real number:"<<d.real;return output;
}
#endif//main.cpp
#include <iostream>
using namespace std;
#include "Base1.h"
#include <Base2.h>
#include <Derived.h>int main()
{Base1 b1(10);Base2 b2('k');Derived d(2, 'A', 2.5);
}
8.5.2 虚继承
C++中,一个类不能被多次说明为一个派生类的直接基类,便可以不止一次地成为间接基类。
1.非虚继承
class B
{public:int x;//...
};class B1:public B
{//...};class B2:public B1
{//...};class D:public B1, public B2
{public:void fun(){x=0;}
};
类B两次成为类D的间接基类。这意味着,D类对象将生成两份从B类继承的类据成员:由B1继承的B类成员和由B2继承的B类成员。因为在D类对象中有两个继承B类的成员副本,所以称B是非虚基类。
#include <iostream>
using namespace std;
class B
{public:B(){cout<<"Constructor called: B\n";};~B(){cout<<"Destructor called: B\n";}int b;
};class B1:public B
{public:B1(){cout<<"Constructor called: B1\n";}~B1(){cout<<"Destructor called: B1\n";}int b1;
};class B2:public B
{public:B2(){cout<<"Constructor called: B2\n";}~B2(){cout<<"Destructor called: B2\n";}int b2;
};class D:public B1, public B2
{public:D(){cout<<"Constructor called: D\n";}~D(){cout<<"Destructor called: D\n";}int d;
};void test()
{D dd;dd.B1::b = 5;dd.B2::b = 10;dd.b1 = 25;dd.b2 = 244;dd.d = 567;cout<<"dd.B1::b="<<dd.B1::b<<"\tdd.B2::b="<<dd.B2::b<<"\n";cout<<"dd.b1="<<dd.b1<<"\tdd.b2="<<dd.b2<<"\tdd.d="<<dd.d<<endl;
}int main()
{test();
}//程序运行结果:
Constructor called: B
Constructor called: B1
Constructor called: B
Constructor called: B2
Constructor called: D
dd.B1::b=5 dd.B2::b=10
dd.b1=25 dd.b2=244 dd.d=567
Destructor called: D
Destructor called: B2
Destructor called: B
Destructor called: B1
Destructor called: B
2.虚继承
通常希望建立D类对象dd时,只要一个dd.b的版本,避免产生二义性。需要把B1和B2对B的继承说明为“虚继承”。在类继承的关键字前添加关键字virtual.
B1,B2类虚继承B类,B是它们的虚基类:
class B
{//...};class B1:virtual public B
{//...};class B2:virtual public B
{//...};
虚继承的测试:
#include <iostream>
using namespace std;class B
{public:B(int x=10){b = x;cout<<"Constructor called:B\n";}~B(){cout<<"Destructor called:B\n";}int b;
};class B1:virtual public B
{public:B1(int x1 = 11, int y1 = 22):B(x1){b1 = y1;cout<<"Constructor called:B1\n";}~B1(){cout<<"Destructor called:B1";}int b1;
};class B2:virtual public B
{public:B2(int x2 = 12, int y2 = 22):B(x2){b2 = y2;cout<<"Constructor called:B2\n";}~B2(){cout<<"Destructor called:B2";}int b2;
};class D:public B1, public B2
{public:D(int i=1, int j1=2, int j2=3,int k=4):B(i),B1(j1),B2(j2){d = k;cout<<"Constructor called:d\n";}~D(){cout<<"Destructor called:D\n";}int d;
};void test()
{D objD;cout<<"objD.b="<<objD.b<<endl;cout<<"objD.b1="<<objD.b1<<"\tobjD.b2"<<objD.b2<<"\tobjD.d"<<objD.d<<endl;B1 objB1; cout<<"objB1.b="<<objB1.b<<"\tobjB1.b1="<<objB1.b1<<endl;
}int main()
{test();
}//运行结果
Constructor called:B
Constructor called:B1
Constructor called:B2
Constructor called:d
objD.b=1
objD.b1=22 objD.b222 objD.d4
Constructor called:B
Constructor called:B1
objB1.b=11 objB1.b1=22
Destructor called:B1
Destructor called:B
Destructor called:D
Destructor called:B2
Destructor called:B1
Destructor called:B
这篇关于C++ 第8章 继承的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!