第一章

例 1-1 hello.cpp

例1-1所示的C++程序,文件扩展名是“.cpp”。

第1行代码作用是包含标准输入/输出头文件iostream。

第2行代码作用是引用标准命名空间std。

第5行代码在屏幕输出“hello C++”。

cout是预定义的输出流对象,用于输出数据;endl表示换行。

例1-2 format.cpp

在例1-2中,第6行代码输出了指定域宽的数据,

第7行代码输出了填充了字符的数据,

第8~11行代码输出的是设置了左对齐和右对齐格式的数据。

例1-3 defaultPara.cpp

在例1-3中,第3~6行代码定义了函数add(),该函数指定了参数y、z的默认值。

第9~11行代码在main()中调用了三次add()函数。

例1-4 overloadFunc.cpp

在例1-4中,第3~14行代码定义了三个重载函数add()。

第17行代码调用add()函数,传入一个double类型的实参10.2。

第18行代码调用add()函数,传入两个int类型的实参1和3。

例1-5 reference.cpp

在例1-5中,第5行代码定义了整型变量a并初始化为10。

第6行代码定义了指向变量a的引用ra。

第7~9行代码分别输出变量a的地址、引用ra的地址、引用ra的值。

例1-6 quote.cpp

在例1-6中,第3~8行代码定义了一个函数exchange(),用于交换两个int类型变量的值。exchange()函数有两个int类型的引用作为参数。

第13~15行代码通过cin从键盘输入两个整型数据给变量a、b,调用exchange()函数交换变量a、b的值,并输出交换结果。

例1-7 allocMeorry.cpp

在例1-7中,第5行代码使用new创建了一个int对象,初始值为10。

第6行代码通过指针pi输出内存中的数据,输出结果为10。

第7~8行代码通过指针pi修改内存中的数据为20,并输出,输出结果为20。

第10~12行代码使用new创建一个大小为10的char类型数组,并通过for循环为数组赋值。

第13~14行代码通过for循环输出数组中的元素,

第16~17行代码使用delete运算符释放int对象和char类型数组对象。

例1-8 mallocStr.h

 

例1-9 mallocStr.c

例1-8和例1-9的mallocStr.h文件和mallocStr.c文件所示代码是C语言程序。其中,mallocStr.c文件中定义了func()函数,在函数内部调用malloc()函数申请一块内存空间存储一个字符串。func()函数第一个参数指定申请内存的大小,第二个参数是存入内存空间的字符串。

例1-10 main.cpp

在例1-10所示的main.cpp中,程序调用了func()函数,则需要使用extern "C" 声明m allocStr.h文件内容以C语言的方式编译。

第二章

例2-1 Student.cpp

下面通过案例演示类的定义、对象的创建及对象的成员访问,如例2-1所示。

在 例2-1 中,第3~10行代码定义了学生类 Student ,该类中有两个公有成员变量 name 和 age ,分别表示学生的姓名和年龄,有两个公有成员函数study()和exam()。

第11~18行代码是在类外实现类的成员函数。

第21~23行代码,在 main() 函数中创建 Student 类对象 stu ,并设置对象 stu 的 name 和 age 值。

第24~26行代码通过对象stu调用对象的成员函数,输出对象stu的信息。

例2-2 package.cpp

例2-2是对例2-1的修改,将Student中的成员变量name和age定义为私有成员,并定义了公有成员函数setNam e()、setAge()、getNam e()和getAge(),分别用于设置和获取对象的姓名和年龄。

第28~37行代码,在实现setAge()时,对传入的参数age进行了判断处理,如果age>100或age<0,则输出“年龄输入错误”的信息,并将 _age 值设置为0。

第48~52行代码,创建对象stu,调用 setName() 函数和 setAge() 函数,分别用于设置对象stu的 _name和 _age;调用 getName() 函数和 getAge() 函数,分别用于获取对象stu的 _name和 _age。

第56~60行代码,创建Student类对象stu1,设置其姓名和年龄,并获取对象stu1的姓名和年龄将其输出。

当设置对象stu的年龄为-20时,程序提示年龄输入错误,并将表示年龄的 _age设置为0;当设置对象stu1的 _age为22时,程序正确输出对象stu1的年龄。

在例2-2中, _name 和 _age 成员为私有成员,因此不能通过对象直接访问,如果仍然像例2-1中的第22~24行代码一样,直接通过对象访问 _name 和 _age,编译器会报错。

例2-3 noPara.cpp

在例2-3中,第7行代码声明了一个无参构造函数;

第14~19行代码在类外实现构造函数,在构造函数体中直接将初始值赋给成员变量;

第28~30行代码在main()函数中创建了对象clock,并通过对象调用showTime()成员函数显示初始化时间。

对象clock的初始化时间为00:00:00,因为创建clock对象调用的是无参构造函数,无参构造函数将时、分、秒都初始化为0。

例2-4 parameter.cpp

第14~19行代码在类外实现有参构造函数,将参数赋值给成员变量,在创建对象时调用有参构造函数,用户可以传入初始值(参数)完成对象初始化。

第28~33行代码,创建了两个Clock对象clock1和clock2,这两个对象在创建时,传入了不同的参数,因此各个对象调用成员函数showTime()显示的初始化时间是不一样的。

需要注意的是,在实现构造函数时,除了在函数体中初始化成员变量,还可以通过“:”运算符在构造函数后面初始化成员变量,这种方式称为列表初始化,其格式如下所示:

在例2-4中,使用列表初始化实现成员变量初始化的方式如下所示:

例2-5 overload.cpp

运行例2-5时,编译器会报错。

在例2-5中,第8行代码声明了一个构造函数,该构造函数有两个参数;

第9行代码声明了一个构造函数,该构造函数有三个参数,且第三个参数有默认值;

第16~27行代码,在类外实现了这两个构造函数;

第36行代码,在main()函数中创建一个对象clock,传入两个参数,编译器无法确认调用的是第8行的构造函数还是第9行的构造函数,因此无法通过编译。

例2-6 Student.cpp

在例2-6中,第3~12行代码定义了出生日期类Birth,该类有3个成员变量,分别是 _year、 _month、 _day,并且定义了有参数的构造函数;

第24~34行代码定义了学生类Student,该类有3个成员变量,分别是name、id、birth,其中birth是类Birth的对象。此外,Student类还定义了一个构造函数。由于成员对象birth的构造函数有3个参数,这3个参数要从类Student的构造函数中获取,因此Student类的构造函数共有5个参数。

第36~42行代码用于实现Student类的构造函数,birth成员对象必须通过“:”运算符在Student构造函数后面初始化,无法在Student构造函数体中赋值。

第52~53行代码,在main()函数中创建Student类对象stu,并通过对象stu调用成员函数show()显示学生信息。

创建对象stu时,先调用Birth类构造函数,之后才调用Student类构造函数。

例2-7 Rabbit.cpp

在例2-7中,第4~13行代码,定义了一个兔子类Rabbit,该类有两个成员变量,分别是name、food,有一个构造函数、一个析构函数和一个普通成员函数eat();

第14~21行代码在类外实现构造函数。在实现构造函数时,由于第二个成员变量 _food是字符指针变量,因此在赋值时,要先使用 new运算符为 _food指针申请一块内存空间并初始化,再将参数pf指向的数据复制到 _food 指向的空间;

第22~25行代码在类外实现eat()函数;

第26~31行代码在类外实现析构函数,在析构函数中,使用delete运算符释放_food指向的内存空间。

第34~37行代码,在main()函数中,分别创建两个对象A和B,然后调用成员函数eat()实现吃食物的功能。

在创建对象的过程中,对象A与对象B除了对象本身所占用的内存空间,还各自拥有一块new运算符在堆上申请的空间。

程序运行结束后,编译器会调用析构函数释放对象资源,在释放时,先释放_food指向的内存空间,再释放对象所占用的内存空间。

程序调用析构函数析构的顺序是先析构对象B,再析构对象A。

例2-8 copy.cpp

在例2-8中,第3~13行代码定义了一个绵羊类Sheep,该类有两个成员变量,分别是 _name、 _color。此外,该类还声明了有参构造函数、拷贝构造函数、普通成员函数show()和析构函数;

第20~25行代码,在类外实现拷贝构造函数,在函数体中,将形参sheepA的成员变量值赋给类的成员变量;

第37~39行代码,在main()函数,创建了Sheep类对象sheepA,并输出sheepA的信息;

第40行代码创建Sheep类对象sheepB,并使用对象sheepA初始化对象sheepB,在这个过程中编译器会调用拷贝构造函数;

第41~42行代码输出对象sheepB的信息。

对象sheepA与对象sheepB的信息是相同的。程序首先调用构造函数创建了对象sheepA,然后调用拷贝构造函数创建了对象sheepB。程序运行结束之后,调用析构函数先析构对象sheepB,然后析构对象sheepA。

例2-9 simple.cpp

运行例2-9中的程序,程序抛出异常,在第43行代码处触发异常断点。

例2-9是对例2-8的修改,在绵羊类Sheep中增加了一个char类型的指针变量成员 _home,用于表示绵羊对象的家。增加了 _hom e成员变量之后,类Sheep的构造函数、拷贝构造函数、析构函数都进行了相应修改。

第17~27行代码实现构造函数,在构造函数内部,首先为_home指针申请堆内存空间,然后调用strcpy()函数将形参home的内容复制到 _home指向的空间。

第28~34行代码实现拷贝构造函数,在拷贝构造函数内部,对指针成员只进行了简单的赋值操作,即浅拷贝。

第39~44行代码实现析构函数,在析构函数内部,使用delete运算符释放_home指向的内存空间。

第47~53行代码,在main()函数中,先创建对象sheepA,再创建对象sheepB,并用对象sheepA初始化对象sheepB。

在这个过程中,使用对象sheepA初始化对象sheepB是浅拷贝过程,因为对象sheepB的 _home指针指向的是对象sheepA的 _home指针指向的空间。

在浅拷贝过程中,对象sheepA中的 home指针与对象sheepB中的 _ home指针指向同一块内存空间。当程序运行结束时,析构函数释放对象所占用资源,析构函数先析构对象sheepB,后析构对象sheepA。在析构sheepB对象时释放了home指向的堆内存空间的数据,当析构sheepA时_home指向的堆内存空间已经释放,再次释放内存空间的资源,程序运行异常终止,即存储“beijing”的堆内存空间被释放了两次,因此程序抛出异常,这种现象被称重析构(double free)。

例2-10 constMember.cpp

 

运行例2-10,编译器会报错

在例2-10中,第3~12行代码定义了一个类Person,该类有三个常成员变量: _name、 _age和 _addr。

第14~21行代码,在类外实现类的构造函数,类的常成员变量在构造函数中完成初始化,即创建对象时完成初始化。

第25行代码创建Person类对象p1,在创建对象时完成了三个常成员变量的初始化。这是创建对象后初始化常成员变量的唯一机会,常成员变量一旦初始化就不能再改变。第26行代码试图修改常成员变量,因此程序会报错。

例2-11 constFunc.cpp

在例2-11中,第3~16行代码定义了Person类,该类中定义了三个成员变量,其中 _name和 _age是常成员函数。

此外,第9~10行代码声明了Person类两个重载的成员函数myInfor(),第9行代码的myInfor()函数为常成员函数,第10行代码的myInfor()函数为普通成员函数。

第22~27行代码在类外实现常成员函数myInfor(),在函数内部,输出各个成员变量的值。需要注意的是,类的常成员函数不能修改成员变量的值,也不能调用非常成员函数,如第24~25行代码,如果取消注释,程序就会报错。

第29~35行代码在类外实现普通成员函数myInfor(),在函数内部,可以像第31行代码那样修改成员变量的值,也可以像第34行代码那样调用非常成员函数。

第42~43行代码创建对象p1,并通过p1调用myInfor()函数,由图2-16可知,对象p1调用的是普通成员函数m yInfor()。

第44~45行代码创建常对象p2,并通过p2调用myInfor(),由图2-16可知,常对象p2成功调用了常成员函数myInfor()。

例2-12 staticMember.cpp

在例2-12中,第3~11行代码定义了学生类Student,其中,第8行代码定义了静态成员变量sum。

第13~17行代码在类外部实现有参构造函数,每当创建对象时,sum的值自动加1,用于统计建立Student类对象的数目。

第19行代码在类外部初始化sum的值为0。

第22~23行代码创建了两个对象stu1和stu2。

第24~25行代码通过对象stu1和stu2访问静态成员变量 _sum,对象stu1和对象stu2访问到的静态成员变量 _sum值均为2。

第26行代码通过类的作用域访问静态成员变量sum,通过类的作用域访问到的静态成员变量sum值也为2。

第27行代码计算对象stu1的大小,对象stu1的大小为28,静态成员变量并不包含在对象中。

例2-13 staticFunc.cpp

 

例2-13中,第4~14行代码定义了类Point,其中,第9行代码定义了静态成员函数getLen(),用于获取两个坐标点之间的距离;第10行代码定义了静态成员变量len,用于存储两个坐标点之间的距离。

第16~19行代码在类外实现有参构造函数,初始化坐标点的值,默认值为0。

第21~27行代码,在类外实现getLen()函数,计算传入的两个坐标p1和p2之间的距离,并将结果保存到变量_len中。

第30~31行代码初始化坐标点p1和p2。

第32行代码调用getLen()函数计算两个坐标点之间的距离。

例2-14 friendFunc.cpp

 

在例2-14中,第3~12行代码定义了关于圆的类Circle,其中圆的半径_radius和圆周率PI是私有成员;

第5行代码在类中声明了友元函数getArea(),用于计算圆的面积;

第18~26行代码是getArea()函数的实现;

第29行代码创建对象circle,并初始化圆的半径为10;

第30行代码调用友元函数getArea()计算圆的面积,面积计算完成后,修改圆的半径为1。

普通函数作为友元函数访问了类中的私有成员,且具有修改私有成员的权限。

例2-15 friendMember.cpp

在例2-15中,第4行代码声明类Point;

第5~11行代码定义了圆类Circle;

第12~22行代码定义了坐标点类Point,其中第15行代码将Circle类中的成员函数getArea()声明为友元函数。

第28~35行代码是getArea()函数的实现,函数的参数为Point类对象的引用,该函数计算两个坐标点距离的绝对值,然后以距离作为圆的半径,计算圆的面积后返回。

其中,第34行在计算圆的面积时访问了Circle类中的私有成员PI。

第38~39行代码初始化坐标点p1和p2。

第40~41行代码,创建对象circle,并通过对象circle调用友元函数getArea()计算圆的面积。

例2-16 friendClass.cpp

在例2-16中,第3~10行代码定义了Tim e类,该类有三个成员变量hour、m inute和_second,分别表示时、分、秒;此外,Time类还声明了Date友元类;

第11~18行代码定义了Date类,Date类有三个成员变量 _year、 _month和 _day,分别用于表示年、月、日。

第19~30行代码在类外实现Date类的构造函数和成员函数showTime();

第31~36行代码在类外实现Tim e类的构造函数;第39~40行代码分别创建对象tim e和date;

第41行代码通过对象date调用成员函数showTime(),并以对象time作为参数。

第四章

例4-1 derive.cpp

为了让读者更好地理解和掌握继承的概念,下面通过案例演示派生类的定义与调用,如例4-1所示。

在例4-1中,第3~7行代码定义了一个动物类Animal,该类中有一个成员函数move(),用于表示动物的行为;

第12~19行代码定义了一个猫类Cat,该类公有继承自Animal类;

第30行代码,在main()函数中创建了猫类对象cat;

第31行代码,通过对象cat调用基类成员函数move();

第32行代码,通过对象cat调用Cat类成员函数walk()。

在例4-1中,Cat类中并没有定义move()函数,但是Cat类继承了Animal类,它会继承Animal类的move()函数,因此Cat类对象能够调用move()函数。

例4-2 public.cpp

下面通过案例演示类的公有继承,如例4-2所示。

 

在例4-2中,第3~14行代码定义了学生类Student,该类声明了私有成员变量_name表示姓名,保护成员变量 _grade表示年级。Student类还定义了4个公有成员函数,分别用于设置、获取学生姓名和年级。

第31~38行代码定义大学生类Undergraduate公有继承Student类。Undergraduate类定义了私有成员变量 _major表示学生专业,此外,还定义了构造函数和显示学生信息的show()函数。

第53~55行代码,在main()函数中创建Undergraduate类对象stu,并通过对象stu调用基类的setGrade()函数、setNam e()函数,用来设置学生的年级和姓名。

第56行代码通过对象stu调用show()函数显示学生信息。

需要注意的是,在例4-2中,第46~47行代码,在Undergraduate类的show()函数内部直接访问了从基类继承过来的保护成员 _grade,因为Undergraduate类是公有继承Student类, _grade在派生类Undergraduate中也是保护成员,所以可以通过成员函数show()访问。但是,show()函数无法直接访问从基类继承过来的 _name成员,因为 _name是基类的私有成员,在派生类中, _name变成了派生类的不可访问成员。所以在show()函数中只能通过基类的公有成员函数getName()访问 _name成员。如果在show()函数中直接访问从基类继承过来的 _name成员,程序会报错。

例如,若在show()函数中添加如下代码:

再次运行程序,编译器会报错。

例4-3 compatibility.cpp

下面通过案例演示基类与派生类之间的类型兼容,如例4-3所示。

 

在例4-3中,第3~10行代码定义了Base类,该类有一个保护成员变量_name;此外Base类还定义了构造函数和普通成员函数show()。

第19~24行代码定义了Derive类,Derive类公有继承Base类;Derive类中定义了构造函数和普通成员函数display()。

第33~36行代码定义了一个函数func(),该函数有一个Base类的指针作为参数,在函数内部,通过Base类指针调用show()函数。

第39行代码,在main()函数中创建了Derive类对象derive;

第40行代码创建Base类对象base,使用对象derive为其赋值;

第41行代码创建Base类对象的引用,使用derive对象为其赋值;

第42行代码定义Base类指针,取对象derive的地址为其赋值。

第43~45行代码分别通过Base类对象、Base类对象的引用、Base类指针调用show()函数;

第46行代码调用func()函数,并取对象derive的地址作为实参传递。

例4-4 conDestructor.cpp

下面通过案例演示派生类构造函数与析构函数的定义与调用,如例4-4所示。

在例4-4中,第3~12行代码定义了发动机类Engine,该类定义了两个私有成员变量type和power,分别表示发动机型号和功率;此外,Engine类还声明了构造函数、普通成员函数show()和析构函数。其中,show()函数用于显示发动机信息。

第13~26行代码,在Engine类外实现各个函数。

第27~36行代码定义了交通工具类Vehicle,该类有一个私有成员变量_name,用于表示交通工具的名称;此外,Vehicle类还声明了构造函数、普通成员函数run()、普通成员函数getNam e()和析构函数。

第37~53行代码在Vehicle类外实现各个函数。

第55~67行代码定义小汽车类Car,Car类公有继承Vehicle类。Car类定义了两个私有成员变量 _seats和 _color,分别表示小汽车的座位数量和颜色。此外,Car类还包含Engine类对象engine,该成员对象为公有成员变量。除了成员变量,Car类还声明了构造函数、普通成员函数brake()、普通成员函数display()和析构函数。

第69~87行代码在Car类外实现各个函数。其中,第69~75行代码实现Car类的构造函数,Car类的构造函数有5个参数,用于初始化成员对象engine、基类Vehicle对象和本类对象。

第90行代码,在main()函数中创建Car类对象car,传入5个参数。

第91~93行代码通过对象car调用基类的run()函数、本类的brake()函数和display()函数实现小汽车各种功能。

第95行代码通过对象car中的公有成员对象engine调用Engine类的show()函数,显示小汽车发动机信息。

派生类构造函数完成了本类对象、成员对象和基类对象的初始化。创建派生类对象时,先调用基类构造函数,再调用成员对象的构造函数,最后调用派生类的构造函数。在析构时,先调用派生类的析构函数,再调用成员对象的析构函数,最后调用基类的析构函数。

例4-5 overwrite.cpp

下面通过案例演示在派生类中隐藏基类成员函数的方法,如例4-5所示。

在例4-5中,第3~7行代码定义了交通工具类Vehicle,该类声明了普通成员函数run(),用于实现交通工具的行驶功能。

第8~11行代码在类外实现run()函数。

第12~16行代码定义了小汽车类Car公有继承交通工具类Vehicle,该类也定义了run()函数,对基类的run()函数进行改写。

第17~20行代码实现Car类的run()函数。

第23行代码,在main()函数中创建Car类对象car。

第24行代码,通过对象car调用run()函数,此次调用的是Car类改写后的run()函数。

第25行代码,通过作用域限定符“::”调用基类的run()函数。

第26~27行代码,定义Vehicle类指针pv,取对象car的地址为其赋值。通过pv指针调用run()函数,只能调用Vehicle类的run()函数,无法调用派生类Car改写的run()函数。

需要注意的是,只要是同名函数,无论参数列表和返回值类型是否相同,基类同名函数都会被隐藏。若基类中有多个重载函数,派生类中有同名函数,则基类中所有同名函数在派生类中都会被隐藏。

例4-6 multi-inherit.cpp

下面通过案例演示多继承派生类构造函数与析构函数的定义与调用,如例4-6所示。

在例4-6中,第3~8行代码定义了木材类Wood,该类定义了构造函数与析构函数。

第9~15行代码定义了沙发类Sofa,该类定义了构造函数、析构函数和普通成员函数sit()。

第16~22行代码定义了床类Bed,该类定义了构造函数、析构函数和普通成员函数sleep()。

第23~29行代码定义了沙发床类Sofabed,该类公有继承Sofa类和Bed类。Sofabed类中包含Wood类对象pearwood;此外,Sofabed类还定义了构造函数与析构函数。

第32行代码,在main()函数中创建了Sofabed类对象sbed;

第33行代码通过对象sbed调用基类Sofa的sit()函数;

第34行代码通过对象sbed调用基类Bed的sleep()函数。

对象sbed成功调用了基类的sit()函数与sleep()函数。在对象sbed创建和析构的过程中,构造函数的调用顺序如下:按照基类的继承顺序,先调用Sofa类构造函数,再调用Bed类构造函数;调用完基类构造函数之后,调用派生类Sofabed中的成员对象(Wood类)的构造函数,最后调用派生类Sofabed的构造函数。在析构时,析构函数的调用顺序与构造函数相反。

例4-7 triangle.cpp

下面通过案例演示派生类对象访问基类同名成员函数时产生的二义性问题,如例4-7所示。

运行例4-7程序,编译器报错。程序错误原因是rest()函数调用不明确。

在例4-7中,第3~7行代码定义了沙发类Sofa,该类定义了公有成员函数rest()。

第8~12行代码定义了床类Bed,该类也定义了公有成员函数rest()。

第13~17行代码定义了沙发床类Sofabed,该类公有继承Sofa类和Bed类。

第20行代码,在main()函数中创建Sofabed类对象sbed。第21行代码通过对象sbed调用基类的rest()函数,由于基类Sofa和基类Bed中都定义了rest()函数,因此对象sbed调用rest()函数时会产生二义性。

在派生类Sofabed中有两个rest()函数,因此在调用时产生了歧义。多继承的这种二义性可以通过作用域限定符“::”指定调用的是哪个基类的函数,可以将例4-7中第21行代码替换为如下两行代码:

 

通过上述方式明确了所调用的函数,即可消除二义性。这需要程序设计者了解类的继承层次结构,相应增加了开发难度。

例4-8 diamond.cpp

下面通过案例演示多重继承中成员变量产生的访问二义性问题,如例4-8所示。

运行例4-8,编译器报错。

在例4-8中,第3~9行代码定义了家具类Furniture,该类定义了保护成员变量_wood,表示家具材质,还定义了构造函数。

第10~13行代码在Furniture类外实现构造函数。

第14~20行代码定义了沙发类Sofa公有继承Furniture类,Sofa类定义了保护成员变量length,表示沙发长度。此外,Sofa类还定义了构造函数。

第22~25行代码在Sofa类外实现构造函数。

第26~32行代码定义了床类Bed公有继承Furniture类,Bed类定义了保护成员变量 _width,表示床的宽度;此外,Bed还定义了构造函数。

第34~37行代码在Bed类外实现构造函数。

第38~44行代码定义了沙发床类Sofabed,该类公有继承Sofa类和Bed类。

基类Furniture的成员变量 _wood在Sofabed类中有两份拷贝,分别通过继承Sofa类和Bed类获得。创建Sofabed类对象时,两份拷贝都获得数据。

在例4-8中,第58~59行代码,创建Sofabed类对象sbed,并通过对象sbed调用getSize()函数获取沙发床信息。在getSize()函数中,第54行代码通过cout输出wood成员值,由于sbed对象中有两个wood成员值,在访问时出现了二义性,因此编译器报错。为了避免访问_wood成员产生的二义性,必须通过作用域限定符“::”指定访问的是哪个基类的 _wood成员。可以将例4-8中的第54行代码替换为如下两行代码:

例4-9 virtualInherit.cpp

下面通过修改例4-8的代码,让Sofa类和Bed类虚继承Furniture类,演示虚继承的作用,如例4-9所示。

在例4-9中,第14~20行代码定义了沙发类Sofa,Sofa类虚继承Furniture类。

第26~32行代码定义床类Bed,Bed类虚继承Furniture类。

第38~44行代码定义沙发床类Sofabed,Sofabed公有继承Sofa类和Bed类。

第58~59行代码,创建Sofabed类对象sbed,并通过对象sbed调用getSize()函数获取沙发床大小。

在Sofabed类的getSize()函数中,第54行代码直接访问了_wood成员,但编译器并没有报错。这是因为在对象sbed中只有一个 _wood成员数据。