[10-21 14:24:02] 来源:http://www.592dz.com EDA/PLD 阅读:9295次
概要:m &inParam );virtual ~User();private:PointerMember *mPointerMember;};// User.cpp#include "User.h"User::User( const RefParam &inParam ): mPointerMember( new PointerMember( inParam ) ){return;}User::~User(){delete mPointerMember;return;}这样当你要使用成员变量时,原来使用mValMember.Something的地方就要用mPointerMember->Something了。文本编辑器或者集成开发环境的查询替换方法可以很容易地在切换存储方法。初始化列表注意,在构造函数初始化列表中初始化对象的指针成员(可以是任何类型成员)是非常重要的。对于C++的初学者来说,像上面的例子中所看到的,下面语句位于大括号之前看起来感觉非常别扭。: mPointerMember( new PointerMember( inParam ) )在类对象的生命周期中,如果实际应用时不需要经常使用指针成员变量时,可以选择将该指针成员初始化为nil(注意:删除一个
在C语言中如何存储并初始化成员变量,http://www.592dz.com成员变量必须在构造函数的初始化列表中完成初始化。Smart pointer members minimize dependencies while allowing exception safety。
通过以指针存储成员变量的方法最小化依赖
当成员变量的头文件非常大或者非常复杂;或者当你有大量的数据成员,并且不想减慢编译速度和强化相互依赖时。你会怎么做?简单来说就是将成员变量保存为指针形式,并用在类的构造函数中使用new为其分配空间。(在某种特殊的情况下,可用引用形式的成员变量代替)。同样要确保在析构函数中删除它们。下面是一段雏形代码。
// User.h
class PointerMember;
class RefParam;
class User
{
public:
User( const RefParam &inParam );
virtual ~User();
private:
PointerMember *mPointerMember;
};
// User.cpp
#include "User.h"
User::User( const RefParam &inParam )
: mPointerMember( new PointerMember( inParam ) )
{
return;
}
User::~User()
{
delete mPointerMember;
return;
}
这样当你要使用成员变量时,原来使用mValMember.Something的地方就要用mPointerMember->Something了。文本编辑器或者集成开发环境的查询替换方法可以很容易地在切换存储方法。
初始化列表
注意,在构造函数初始化列表中初始化对象的指针成员(可以是任何类型成员)是非常重要的。对于C++的初学者来说,像上面的例子中所看到的,下面语句位于大括号之前看起来感觉非常别扭。
: mPointerMember( new PointerMember( inParam ) )
在类对象的生命周期中,如果实际应用时不需要经常使用指针成员变量时,可以选择将该指针成员初始化为nil(注意:删除一个nil指针永远是安全的。因为delete方法的实现在将指针变量传递给堆管理器前,首先检验指针的值)。如果指针变量需要在构造之前分配存储空间的话,一定要在初始化列表中完成,而不像下面代码一样在构造函数体中完成。
User::User( const RefParam &inParam )
{
mPointerMember = new PointerMember( inParam ); // DON'T DO THIS
return;
}
我所工作的大型C++项目中,那些很少使用初始化列表初始化成员变量的,都到处充斥着错误。其中有一个项目,源码共70多兆,我在那家公司工作的时候除了调试错误没做其他任何事情。搞定了一摞错误,又会出现一筐错误。适当的初始化成员变量失败不只是代码的问题,还与更高层次问题相关。
一般来说,构造函数体应该只用来开展对成员变量的操作,或者是全部完成初始化后对整个对象的操作。基本原则是保留函数体给不适合由初始化列表完成的代码。
开始学习适当的使用初始化列表以来,在写信构造函数或者重写老的构造函数后,函数体往往是空的,或者仅包含不多的几行代码,因为全部的实际工作都在初始化列表中完成了。要完成这些工作有时候需要一些额外的工作,但是最后还是能把这些工作量找回来的。
注意了,初始化列表是引用型和常量型成员变量初始化的唯一地方,如果在初始化列表中初始常量成员变量失败了,它可能已在默认构造函数中初始化了,你在其他任何地方都不能改变它,构造函数体也不例外。如果以这种方式初始化引用型成员变量失败,代码就不能通过编译。下面的代码在g++中将发生致命错误。
class HasRefMember
{
public:
HasRefMember( int &inIntToAlias );
private:
int &mSomebodyElsesInt;
};
HasRefMember::HasRefMember( int &inIntToAlias ) // No
initialization list!
{ // refinit.cpp: In method `HasRefMember::HasRefMember(int &)':
// refinit.cpp:11: uninitialized reference member `HasRefMember::mSomebodyElsesInt'
mSomebodyElsesInt = inIntToAlias;// The compiler doesn't even get this far
}
关于初始化列表的其他一些注意事项:在初始化列表中,成员变量的初始化顺序要与类型生命中的顺序一致。实际情况下,C++编译器总是按照变量声明的顺序初始化成员变量。初始化列表顺序与声明相匹配将避免混淆。进一步说,如果你理解了成员变量按照一定的顺序初始化的话,你可以安排顺序使得构造函数的后面的变量使用前面的变量作为参数。如下例所示。
class Example
{
public:
Example( double inVal );
private:
double mSqrt;
double m2Sqrt;
};
Example::Example( double inVal )
: mSqrt( sqrt( inVal ) ),
m2Sqrt( mSqrt * 2 )
{
return;
}
如果我们改变了了成员变量的声明顺序,但是保留初始化列表原样不变的话,m2Sqrt将初始化为内存残留的垃圾值(一个未定义的值),mSqrt将初始化为inVal的平方根。
因此,如果要改变类声明中成员变量的顺序的话,确保同时检验并更新初始化列表。周期性地检验程序中的构造函数,以确保成员变量的正确顺序。一些编译器会非常友好的提示发生的顺序错误。
对于初始化列表中的一些语法方面的局限性,需要另外的一些工作才能行得通。
列表中的每一项都是一次对成员变量构造函数的调用。某些类型看起来没有调用构造函数,但你可以用一个值或者相同类型的对象的引用(你调用了拷贝构造函数)来初始化它。如果你没有重写自己的拷贝构造函数的话,编译器将会提供一个默认的拷贝构造函数(尽管默认构造函数不能提供正确的功能)。仅仅分配了内置数据类型的构造。