我们还是先来看看上次遗留的问题。“为什么(上次异常是三个,这次是六个,可以解释吗)?怎么办?”这其中的原因,我想您是明白的,我只做简单的重复:)。代码段中:
//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” : 没有合适的默认构造函数可用
总之我想,这样的观念是必须的:
初始化工作必须完成。任何情况下,系统完成初始化工作的机理是不变的(构造函数调用顺序),系统不会偷懒:)。我们需要设计良好的初始化过程。
这次遗留的问题是,所有的这些问题,在组合的情况下,也会发生吗?在对象数组初始化时,如果我们希望每个对象的初始化状态都不同,应该如何做?如果您感觉不够清晰,愿您能写代码测试一下,跟着结果思考:)。