我们还是先来看看上次遗留的问题。“为什么(上次异常是三个,这次是六个,可以解释吗)?怎么办?”这其中的原因,我想您是明白的,我只做简单的重复:)。代码段中:
//bool IsSameMan(Human one,Human another) if(IsSameMan(lucy,lily))//(1) { std::cout<<"\n They are the same one.\n"; }else std::cout<<"\n No,they're not the same one.\n";
} (1)句传入两个对象(lucy,lily),根据Human类的定义,系统试图把它们的资源分别复制给one和 another,但毫无疑问出现了浅拷贝的问题。当局部对象one和another析构时,问题表现出来了。至于为什么是六个异常,呵呵,还是您来回答吧? 知道原因,就容易解决。我们需要一个拷贝构造函数,来完成这项资源拷贝的工作,如下:
If add...in class Human(public):
Human(Human &human):ID(human.getID()) { name=new Name(human.getName()->getName()); }
OK,and you get :
They are the same one. Press any key to continue 就没问题了。
这次,我们来看看组合与继承中的初始化问题,最后再说明对象数组初始化需要注意的地方。主要是继承的例子。我想澄清一些想法,强调一些观念。当然,如果有争议的地方,欢迎您给我指出。先谢过了。 大家知道,组合与继承都是非常重要的耦合方式。典型的组合与继承的例子,分别如下:
class Point{ int x,y; //... public: //... };
class Shape{ protected: Point p0; //组合 //... public: Shape(int x,int y); Shape(Point &point); //... };
class Window:public Shape{ //继承 int width,height; //... public: Window(int _x,int _y,int _w,int _h); //... }; 在初始化时,可能是这样的:
Window::Window(int _x,int _y,int _w,int _h):Shape(_x,_y),width(_w),height(_h) { } //(1) 组合与继承的初始化,如果是我们自己来做这项工作,初始化的时机可只有这一个,(1)冒号后。这是c++的语法,没有什么可说的。 但是,我想,到现在,有些认识,需要得到强调。 首先,c++中的对象创建时必须得到初始化。一般的,我们要自己来做这项工作;但如果您没有给出自己的构造函数,系统会给一个默认的构造函数,并去调用它(您知道它会做些什么吗?)。初始化是必须保证的。
其次,复杂类(组合继承迩来)的初始化工作中,构造函数调用有固定的顺序。一般的c++教材都说的很详细,我就不多说了。 但有趣的是,复杂类中,若出现默认的系统初始化,都会发生些什么?如果所有的父类都没有写构造函数,成员对象的类也没有写构造函数,又没有常量成员和引用成员,一系列的初始化工作是按原来的顺序执行,还是没有初始化?这个问题也许没有实际意义,但对理解初始化工作是很有意义的,我认为。
我们以继承的初始化为例子,一步一步来看。
我们先来看看,如果不写构造函数,系统会做什么。 类的定义。首先,Shape是一个抽象基类(接口)。我没有为它写任何构造函数,让系统来吧。
//shape.h #pragma once
class Shape { protected: int x,y; public: virtual ~Shape(void); virtual void SetXY(int _x,int _y) = 0; }; //shape.cpp #include "shape.h" #using <mscorlib.dll> #include <stdlib.h>
Shape::~Shape(void) { } 下面是Shape类的一个子类实现,Window。为了对比测试,它有两个构造函数,一个是无任何参数,不做任何事情,当然,我们可以假设不知道它会不会调用父类Shape的构造函数。另一个是正常的构造函数,但从父类
继承的成员没有在其中初始化(看系统如何做)。 //window.h #pragma once #include "shape.h"
class Window : public Shape { int width,height; public: Window(void); Window(int _x,int _y,int _w,int _h); virtual ~Window(void); virtual void SetXY(int _x, int _y);
void Test(void); }; //window.cpp #include<iostream> //构造函数1 Window::Window(void) { } //构造函数2 Window::Window(int _x,int _y,int _w,int _h) { width=_w; height=_h; }
Window::~Window(void) { }
void Window::SetXY(int _x, int _y) { x=_x; y=_y; } // void Window::Test(void) { std::cout<<"x="<<x<<",y="<<y<<",width="<<width<<",height="<<height<<"\n\n"; }
这是公共的测试文件: //fmain.cpp #include <iostream> #include "window.h" void main() { Window win2; std::cout<<"Test:win2:\n"; win2.Test();
Window win(10,10,50,50); std::cout<<"Test:win:\n"; win.Test(); }
执行,结果如下:
Test:win2: x=1243328,y=1243040,width=1303984,height=1243296
Test:win: x=1243040,y=1,width=50,height=50
Press any key to continue
win2调用的是构造函数1,win调用的是构造函数2。win2的所有成员,值都似乎是随机给的。而win中,只有width和height达到了预期效果,x和y和win2没有什么区别! 暂行记住,这是没有写构造函数的结果。
我们现在给Shape类添加构造函数,有两个版本,作用同于上面Window类的构造函数。如下: //shape.h #pragma once
class Shape { protected: int x,y; public: Shape(void); Shape(int _x,int _y); virtual ~Shape(void); virtual void SetXY(int _x,int _y) = 0; }; //shape.cpp #include "shape.h" #using <mscorlib.dll> #include <stdlib.h> //构造函数1:什么也不做 Shape::Shape(void) { } //构造函数2:履行初始化工作 Shape::Shape(int _x,int _y) { x=_x; y=_y; }
Shape::~Shape(void) { }
Window类的构造函数相应改正如下: //构造函数1 Window::Window(void):Shape() { } //构造函数2 Window::Window(int _x,int _y,int _w,int _h):Shape(_x,_y) { width=_w; height=_h; }
测试文件代码不变。执行,结果如下:
Test:win2: x=1243328,y=1243040,width=1303984,height=1243296
Test:win: x=10,y=10,width=50,height=50
Press any key to continue win2的情况没有改观,但win的初始化就是我们所要的! 这说明了什么?如果您不明白,可以回头对比看看。
系统的默认初始化,即使它给的值,是当时所在的内存地址的所有值(不可预料),但那仍然是初始化!
说到默认初始化,在欲创建对象数组时要特别注意,如果没有确省的构造函数,对象数组是无法创建的,因为没有合适的构造函数可调用!(如果不能初始化,编译器是做出错处理的)
Shape和Window类的构造函数相应改正如下:
//构造函数1:
Shape::Shape(void) {
x=0;y=0; } //构造函数1 Window::Window(void):Shape() {
width=0;height=0; } 主函数中代码换为:
Window win[5]; for(int i=0;i<5;i++) { std::cout<<"Test:"<<i<<":\n"; win[i].Test(); }
则结果为: Test:0: x=0,y=0,width=0,height=0
Test:1: x=0,y=0,width=0,height=0
Test:2: x=0,y=0,width=0,height=0
Test:3: x=0,y=0,width=0,height=0
Test:4: x=0,y=0,width=0,height=0
Press any key to continue
但是,你把Shape类的构造函数1注释掉(既删除缺省构造函数),再用同样的测试文件编译时,编译出错:
Window.cpp(5) : error C2512: “Shape” : 没有合适的默认构造函数可用
总之我想,这样的观念是必须的: 初始化工作必须完成。任何情况下,系统完成初始化工作的机理是不变的(构造函数调用顺序),系统不会偷懒:)。我们需要设计良好的初始化过程。
这次遗留的问题是,所有的这些问题,在组合的情况下,也会发生吗?在对象数组初始化时,如果我们希望每个对象的初始化状态都不同,应该如何做?如果您感觉不够清晰,愿您能写代码测试一下,跟着结果思考:)。 
|