两个关于构造函数的基本问题
我对于构造函数有两个基本问题:
1.在一个构造函数中调用另外一个构造函数是合法的,比如:
class myclass{
public:
myclass(){...}
myclass(int a){
......
myclass(); //调用另外一个构造函数
......
}
...
};
但是我不知道这样的用法在c++里面常见么?或者说有用到上述情形的时候么?
(我记得java中好像这种情况很常见)
2.为什么constructor不可以是虚函数?以前就听说过这个结论,但是不知道为什么?既然析构函数可以为
虚函数,那构造函数也应该可以阿,否则就不对称了?
问题点数:40、回复次数:12Top
1 楼OOPhaisky(异化$渴望成功~~)回复于 2006-07-01 14:43:42 得分 10
第一个问题:
在java中,这种用法的确很常见,但是在c++中我基本没用过这种方法(或者说的绝对一点:干脆就没用过
),所以也不知道这种用法存在的价值是什么,只能等待高手解答。
但是,提醒楼主:绝对不能在类的外部显式调用constructor,这是C++标准明令禁止的(尽管有些编译器对
此支持的并好)。
第二个问题:
楼主可以从两个层面来理解。
首先,从宏观层面理解:所谓virtual function,就是在运行时刻根据引用或者指针所指向对象的实际类型
来调用相应的虚拟函数。而在执行constructor时,对象尚未构造完毕,也就是说这个对象现在还“不存在
”,根据一个“尚未构造完全的对象”(或者夸张一点,“不存在的对象”)来选则相应的虚拟函数,你不
觉得荒唐么?
其次,从微观层面理解:在对象的constructor中会执行一些初始化对象的操作,不仅仅是你的代码,还有
大量编译器为你添加的初始化代码(比如调用基类的构造函数,调用成员对象的构造函数,初始化vptr等等
),正是这些初始化代码才使得你的对象具有它所属类型的“全部特征”。在这里跟虚拟函数有关的最重要
概念是vptr,在constructor中要指定vptr的值,以使它指向正确的virtual table,进而使得运行时刻得以
“找到”正确的虚拟函数。说到这里,想必楼主已经明白了,在执行构造函数时,vptr尚未被指定正确的值
,那怎么能“找到”正确的“虚拟构造函数”呢?是不是也很荒唐?
至于楼主说的“对称”问题,我的看法是这样的:C++中确实有很多东西是对称的,我们也确实应该鼓励对
称,追求对称,毕竟对称从某种程度上说更符合我们的思维习惯(也许是因为我们人类就是对称的动物吧,
呵呵,玩笑);但是,要想让所有的东西都对称,未免也有些不合理,因为所有的东西都对称了,世界就完
美了,而完美是不存在的,对吧?既然如此,就让我们接受那些不对称的东西,记住那些不对称的东西,就
当是“保持生态多样性”了吧。Top
2 楼OOPhaisky(异化$渴望成功~~)回复于 2006-07-01 14:48:35 得分 0
害怕在提交时断网(多次遇到,造成辛辛苦苦写的答复付诸东流),所以先在记事本里面编辑,然后粘到上面,可是这次不知道怎么回事儿,将格式信息一块带过来了,郁闷,楼主慢慢看吧,内容还是挺连贯的,呵呵,不好意思Top
3 楼lyskyly(浮生三笑)回复于 2006-07-01 15:57:59 得分 2
OOPhaisky(渴望成功) 讲解的很详细
第一个问题也奇怪中,
在C++中喜欢把相同代码放到另一个函数中,构造函数都调用这个函数来初始化相同部分,
Top
4 楼jixingzhong(瞌睡虫·星辰)回复于 2006-07-01 19:33:45 得分 1
欠套容易混乱,
就像 goto 一样,
互相欠套后可能会 ....Top
5 楼cppgreat(渴望成功)回复于 2006-07-01 21:04:48 得分 0
我又发现了新问题,不仅仅可以调用本类的构造函数,还可以调用基类的构造函数,如:
class Base{
public:
Base(){ cout << "base default" << endl; }
Base(int){ cout << "base int" << endl; }
};
class Derived : public Base{
public:
Derived():Base(2){ cout << "derived default" << endl; }
void test();
};
Derived d;
此时输出为:
base int
derived default
说明初始化成员列表中对基类构造函数的显式调用被编译器识别出来了,所以它不再去调用基类的default constructor。但是程序改成如下:
class Base{
public:
Base(){ cout << "base default" << endl; }
Base(int){ cout << "base int" << endl; }
};
class Derived : public Base{
public:
Derived(){ Base(2); cout << "derived default" << endl; }
void test();
};
Derived d;
输出结果为:
base default
base int
derived default
说明在函数体中调用基类的构造函数编译器根本没有察觉,它还是会为你调用基类的default construtctor!
既然如此,C++标准应该明确规定不允许在构造函数中调用构造函数(或在成员函数中调用构造函数),这样才合理,不是么?Top
6 楼fangrk(加把油,伙计!)回复于 2006-07-01 23:07:15 得分 0
myclass(int a){
......
myclass(); //调用另外一个构造函数
......
}
myclass(); 大多数情况下是废话,只是产生一个临时变量而已。在构造函数中调用构造函数有什么意义呢?
你可以写一个初始化的函数,比如init,功能就是构造函数的功能
class myclass{
public:
myclass(){ init();}
myclass(int a){
......
init(); //调用另外一个构造函数
......
}
...
};
Top
7 楼fangrk(加把油,伙计!)回复于 2006-07-01 23:10:05 得分 0
虚构造,可以看看工厂模式Top
8 楼UPCC(杂食动物)回复于 2006-07-01 23:12:03 得分 10
class myclass{
public:
myclass(){...}
myclass(int a){
......
myclass(); //调用另外一个构造函数
......
}
...
};
------------------------------------
在这里使用的“myclass(); //调用另外一个构造函数”
并不是调用本身实例的myclass(),而是在这里生成另一个myclass()的对象。
没有好运的,构造函数是特殊的,相互之间在一个实例里是不可调用Top
9 楼htqx(航天奇侠)回复于 2006-07-02 03:31:00 得分 7
c++语言的特点。
调用构造函数即构造对象。因此在函数构造函数调用构造函数,会产生一个临时对象,而不是作用于当前对象,这和java语言的不同之处。
为何不是虚拟构造。
主要是因为构造过程很统一,就是调用基类的构造后调用自己的构造。编译器已经可以实现这个过程,懂得追朔到类树的根部。这是从下到上的追踪过程,代码本身静态给出了这种关系。
对比一下析购,是调用基类的接口,却要他追踪子类的实现,只有动态才能确定。
子类调用基类的构造,而不是默认的构造。
因为子类构造的过程,必须调用基类构造,而基类构造,可能有参数的。因此语言支持这种机制。
在初始化式上的调用,和函数内部的调用的顺序是不同的。在初始化式调用是在自身构造没有进行之时,而在函数中调用。相当于已经调用了默认的基类构造。函数内调用相当于创建了一个基类的临时对象,而非作用于当前对象。
例子:
using namespace std;
class ca{public: ca(){cout<<"ca():"<<this<<endl;} ca(int){cout<<"ca(int):"<<this<<endl;}};
class cb : public ca{public: cb():ca(1){cout<<"cb():"<<this<<endl;ca();
cout<<"----------------"<<endl;cb(1);}
cb(int){cout<<"cb(int):"<<this<<endl;}};
int main(){
ca *oa = new cb();
cout <<"realAd:"<<oa<<endl;}
ca(int):003A5A20 //初始化表
cb():003A5A20 //自身
ca():0012FCC3 //内部基
----------------
ca():0012FCCF //内部自身:调用默认基
cb(int):0012FCCF //内部自身:自身
realAd:003A5A20 //最后的结果
Top
10 楼fireseed(【VC无敌,英明神武,千秋万代,一统江湖!】—奶油狗)回复于 2006-07-02 04:45:41 得分 10
希望楼主认真研究一下下面的代码
vector<string> vecNames;
class Girl
{
public:
Girl( void )
{
NameMe(); //起名字
PrintMyName(); //打印名字
}
Girl( int )
{
Girl(); //调用自身的构造函数,给自己起名
PrintMyName(); //打印名字
}
void NameMe( void )
{ //给自己起一个唯一的名字
if ( vecNames.size() > 0 )
{ // 如果名字列表中的名字还没取完,则随机取出一个
size_t nIdx = rand() % vecNames.size();
m_strMyName = vecNames[nIdx];
//把取到的名字从列表中删除
vecNames.erase( vecNames.begin() + nIdx );
}
}
void PrintMyName( void )
{ // 打印出自己的名字
cout << "My name is ";
if ( m_strMyName.empty() )
{ // 居然没名字?!这行代码会被执行吗?
cout << "... Oh! I haven't had a name!" << endl;
}
else
{ // 输出名字
cout << m_strMyName << endl;
}
}
private:
string m_strMyName;
};
int _tmain( int argc, _TCHAR* argv[] )
{
vecNames.push_back( "Alice" ); // 名字列表
vecNames.push_back( "Lucy" );
vecNames.push_back( "Nancy" );
vecNames.push_back( "Anne" );
vecNames.push_back( "Louise" );
vecNames.push_back( "Quistis" );
vecNames.push_back( "Candy" );
srand( unsigned(time(0) ) );
Girl a;
Girl b(1);
////////////不可改动//////////////
system( "pause" );
// _CrtDumpMemoryLeaks();
return 0;
////////////不可改动//////////////
}
事实上Girl()只是构造了一个临时对象随后又销毁了,什么也没干。Top
11 楼cppgreat(渴望成功)回复于 2006-07-02 12:45:04 得分 0
懂了,结贴!!!!!!!!Top
12 楼gaoming007(哈哈)回复于 2006-07-02 23:57:23 得分 0
markTop




