??xml version="1.0" encoding="utf-8" standalone="yes"?>久久精品国产亚洲Aⅴ蜜臀色欲,久久亚洲sm情趣捆绑调教,国产精品久久影院http://www.shnenglu.com/xiaohe0817/zh-cnSun, 11 May 2025 08:16:02 GMTSun, 11 May 2025 08:16:02 GMT60Effective C++MW记之三 :定对象被用前已先被初始化http://www.shnenglu.com/xiaohe0817/archive/2008/12/30/70572.htmlEdmundEdmundTue, 30 Dec 2008 03:06:00 GMThttp://www.shnenglu.com/xiaohe0817/archive/2008/12/30/70572.htmlhttp://www.shnenglu.com/xiaohe0817/comments/70572.htmlhttp://www.shnenglu.com/xiaohe0817/archive/2008/12/30/70572.html#Feedback0http://www.shnenglu.com/xiaohe0817/comments/commentRss/70572.htmlhttp://www.shnenglu.com/xiaohe0817/services/trackbacks/70572.htmlint xQ?br>在某些语境条件下保证初始化ؓ0Q而在另外一些语境下却ƈ没有q个保证。而下边这U情况:
class Point{
int x,y;
}
;

Point p;
p的成员函数有的时候被初始化(0Q,有的时候没有被初始化?br>现在Q已l有了一些规则,对象的初始化何时会发生,何时不会发生。但是这些规则过于复杂,Ҏ们的记忆有一定的挑战Q哈哈?br>通常如果使用C part of C++而且初始化可能招致运行期成本Q那׃保证发生初始化。一旦进入non-C parts of C++,规则有一些变化。这是为啥array(C part of C++)不保证其内容被初始化Q而vector(STL part of C++)却有此保证?br>我们的最佛_理状态是Q在使用M对象之前先将它初始化。对于无M成员的内|类型,必须手动完成此事Q?
int x = 0;   //对intq行初始化;
const char *text = " A C-style string";   //Ҏ针进行初始化Q?/span>
double d;
std::cin 
>> d;   //采用input stream的方式完成初始化Q?/span>
至于内置cd以外的其他类型,初始化的责Q落在了构造函数n上。规则很单:保构造函数都对象的每一个成员初始化?br>q个规则很容易奉行,值得注意的是别؜淆了赋?assignment)和初始化(initialization)。考虑一个表现通讯的classQ?
class PhoneNumber{};
class ABEntry{    //Address Book Entry;
public:
ABEntry(
const std::string &name, const std::string &address, const std::list<PhoneNumber> &phones);
private:
std::
string theName;
std::
string tehAddress;
std::list
<PhoneNumber> thePhones;
int numTimesConsulted;
}
;

ABEntry::ABEntry(
const std::string &name, const std::string &address, std::list<PhoneNumber> &phones)
{//以下全是赋?assignment)Q不是初始化(initialization);
theName = name;
theAddress 
= address;
thePhones 
= phones;
numTimesConsulted 
= 0;
}
ABEntry对象会带l你期望的|但不是最佛_法。C++规定Q对象成员变量的初始化动作发生在q入构造函数本体之前。在ABEntry构造函数内QtheName, theAddress, thePhone都不是被初始化,而是被赋倹{初始化的时间发生的更早Q发生于q些成员函数的default构造函数被自动调用之时(比进入ABEntry构造函数本体的旉更早)。但q对numTimesConsulted不ؓ真,因ؓ他属于内|类型,不保证在你看到的那个赋值动作的旉点之前获得初倹{?br>ABEntry构造函数的一个较好写法是Q用所谓的member initializatin list(成员函数初始?替换赋值动作:
ABEntry::ABEntry(const std::string &name, const std::string &address,
 
const std::list<PhoneNumber> &phones)
: theName(name),
 theAddress(address),
 thePhones(phones), 
numTimesConsulted(
0)
{}//构造函数本体不需要Q何动?/span>
q个构造函数和上一个的l果相同Q但是通常效率较高。基于赋值的那个版本首先调用default构造函CؓtheName, theAddress,和thePhones讄初|然后立刻再对他们赋倹{default的一切作为此刻都费了。成员初始列(member initianlization list)的做法避免了q一问题Q因为初始列中针对各个变量设的实参,被拿M为各个成员函数的实参。本例中theName以name为初D行copy构造,theAddress以address为初D行copy构造,thePhones以phones为初D行copy构造?br>对于大多数类型而言Q比起先调用default构造函数再调用copy assignment操作W,单只调用一ơcopy构造函数是比较高效的,有事甚至高的多。对于内|类型,如numTimesConsultedQ其初始化成本和赋值的成本相同Q但Z一致性最好也通过成员初始列来q行初始化。同样道理,如果我们想default构造一个成员变量,也可以用成员初始列Q只要指定nothing作ؓ初始化列p了,假设ABEntry有一个无参构造构造函敎ͼ我们可以实现如下Q?
ABEntry::ABEntry()
:theName(),             
//调用theName的构造函?/span>
theAddress(),           //调用theAddress的构造函?/span>
thePhones(),            //调用thePhomes的构造函?/span>
numTimesConsulted(0)    //记得此内置cd昑ּ定义?
{}
此处记得一条规则,那就是在成员初始列中列出所有的成员变量Q以免还得记住哪些成员变量可以无需初倹{D个例子,如numTimesConsulted属于内置cdQ如?member initialization list)遗漏了他Q他没有初|因而可能开?#8220;不明行?#8221;的潘多拉盒子?br>有些情况下,即面对的是内置cdQ也一定要使用初始列。如果成员变量是const或者referenceQ他们一定得需要赋初|而不是赋倹{ؓ避免成员变量何时必须在成员初始列中初始化Q何时不需要,最单的做法是QL使用成员初始列?br>C++有着十分固定?#8220;成员初始化次?#8221;。是的,ơ序L相同的:base classesL先于derived classes被初始化Q而class的成员变量L以其声明ơ序被初始化。让我们在看一下ABEntryQ其theName永远先被初始化,然后是theAddressQ之后thePhonesQ最后是numTimesConsultedQ即使他们在member initialization list中以不同的顺序出玎ͼ也不会有M影响。ؓ了避免迷惑,你在member initialization list中条列各个成员时Q最好L以其声明ơ序为次序?br>一旦我们已l?很小心的内|型成员变量明确的加以初始化Q而且也确保构造函数运用member initialization list初始化base classes和成员变量,那就剩下唯一的一件事情需要操心,是Q不同编译单元内定义之non-local static对象的初始化ơ序?br>所谓static对象Q其寿命从被构造出来直到程序结束ؓ止,因此stack和heap_based对象都被排除。这里所说的static对象包括global对象Q定义于namespace对象内的对象Q在class内,在函数内Q以及在file内被声明为static的对象。函数内的static对象UCؓlocal static对象Q其他则成ؓnon-local static对象Q程序结束时static对象会自动销毁,也就是他们的析构函数会在main()l束时被调用?br>所谓编译单元(translation unitQ是指生单一目标文gQsingle object fileQ的那些源码。基本上是单一源码文g加上其所包含的头文g?br>现在Q上面所涉及到的问题臛_包括两个源码文gQ每一个内含至一个non-local static对象Q也是说对象是globalQ或者位于namespace内,或者class内,或者file作用域内被声明ؓstaticQ。问题是Q如果某个编译单元内的某个non-local static对象的初始化动作使用了另外一个编译单元内的non-local static对象Q他所使用的这个对象可能没有初始化Q因为C++?#8220;不同~译单元内的non-local static 对象”的初始化ơ序q没有明定义?br>来个例子Q?br> 假设有一个FileSystem classQ它让互联网上的文g看v来像是本机。由于这个class使世界看h像个单一文gpȝQ可能会产生一个特D对象,位于global或namespace作用域内Q象征单一文gpȝQ?br>
class FileSystem{
public:

std::size_t numDisks() 
const;

}
;
extern FileSystem tfs;//预备l客户用的对象Qtfs代表"the file system"
FileSystem对象l不是一个无关痛痒的对象Q因此客户如果在theFileSystem对象构造完成前׃用他Q会得到惨重的代P
现在假设建立了一个class用以处理文gpȝ内部的目录。很自然Q他们会用上theFileSystem的对象:
class Directory{
public:
Directory();

}
;
Directory::Directory()
{

std::size_t disks 
= tfs.numDisks();

}
q一步假设,q些客户军_创徏一个directory对象Q用来放|时文Ӟ
Directory tempDir(params);//Z时文件而做出的目录
现在Q初始化的重要作用显C出来了Q除非tfs在tempDir之前被初始化Q否则tempDir的构造函C用到未初始化的tfs。但是tfs和tempDir是不同的人在不同的时间创建出来的Q他们是定义于不同编译单元内地non-local static对象Q如何能认tfs在tempDir之前先被初始化?
哦,q是无法认的?br>一个小的设计可以改变q种情ŞQ将每一个non-local static 对象搬到自己的专属函数内Q该对象在此函数内被声明为staticQ。这些函数返回一个reference用来指向他所包含的对象。然后用戯用这些函敎ͼ而不是直接指涉这些对象。换句话是non-local static对象被local static对象替换了。这个实现的基础在于QC++保证Q函数内的non-local static 对象会在函数被调用期间首ơ遇上该对象之定义式时被初始化。所以如果以函数调用替换直接讉Knon-local static 对象Q就获得了保证,保证所得的那个reference指向一个经历初始化的对象?此技术实C上面的代码如下:
class FileSystem{};//同前
FileSystem &tfs()//q个函数用来替换tfs对象Q他在FileSystem中可能是个static?/span>
{
static FileSystem fs;
return fs;
}

class Directory{};
Directory::Directory()
{

std::size_t disks 
= tfs().numDisks();

}

Directory 
&tempDir()
{
static Directory td;
return td;
}
修改之后Q我们只是用的是tfs()和tempDir()Q而不是tfs和tempDir。也是使用的是指向static的reference而不是static对象自n?br>q样的reference-returning函数往往十分单纯Q第一行定义ƈ初始化一个local static对象Q第二行q回他。当Ӟ他是l佳的inline候选,特别是经常被调用的话。但是,另一斚wQQ何一Unon-const static对象Q不论是local或者non-localQ在多线E下“{待某事发生都会有麻烦的。处理这个麻烦的一U做法是Q在E序的单U程启动阶段手工调用所有的reference-returning函数Q?Q)Q这可消除与初始化有关的race condition?br>为避免对象初始化之前q早的用他们,你需要做的是三g事:手工初始化内|的non-member对象Q用member initialization listQ在初始化次序不定下加Z的设计?br>

Things to remember:
1.Manually initialize objects of built-in type, because C++only sometimes initializes them itself;
2.In a constructor, prefer to use the member initialization list to assigenment inside the body of the constructor. List data member in the initialization list in the same order they're declared  in the class.
3.Avoid initialization order problems across translation units by replacing non-local static objects with local static objects.

Edmund 2008-12-30 11:06 发表评论
]]>
Effective C++MW记之二 :可能用consthttp://www.shnenglu.com/xiaohe0817/archive/2008/12/09/68989.htmlEdmundEdmundTue, 09 Dec 2008 15:00:00 GMThttp://www.shnenglu.com/xiaohe0817/archive/2008/12/09/68989.htmlhttp://www.shnenglu.com/xiaohe0817/comments/68989.htmlhttp://www.shnenglu.com/xiaohe0817/archive/2008/12/09/68989.html#Feedback0http://www.shnenglu.com/xiaohe0817/comments/commentRss/68989.htmlhttp://www.shnenglu.com/xiaohe0817/services/trackbacks/68989.html条款三:可能用constQuse const whenever possibleQ?br>const允许你指定一个语义约束,而编译器会强制实施这约束。它允许你告诉编译器和其他程序员某值应该保持不变。有一条约束需要注意,那就是:如果const出现?L左边Q那是说被指物是常量;如果出现在星号右边,则表C指针本w是帔RQ如果出现在两边Q则被指物和指针都是帔R。如果被指物是常量,则关键字const写在cd的前面和cd之后Q星号之前两U所表示的语义是相同的。例如下面这两种写法是一LQ?br>void f1(const Widget* pw);
void f2(Widget const * pw);
const也可用来修饰STL中的q代器。声明P代器为const想声明指针为const一P即T* const 指针Q,表示q个q代器不得指向不同的东西。但它所指的东西的值是可以改变的。如果希望P代器所指的东西不可改变Q即模拟一个const T*指针Q,需要的是const_iterator:
std::vector<int> vec;
...
const std::vector<int>::iterator iter = vec.begin();// same as T* const
*iter = 10;                                                        //no problem
++iter;                                                              //wrong!!
std::vector<int>::const_iterator cIter = vec.begin();//same as const T*
*iter = 10;                                                             //wrong!!
++iter;                                                                  //no problem

const 最具威力(Q)的用法是面对函数声明时的应用。在一个函数声明式内,const可以和函数返回|各参敎ͼ函数自nQ成员函敎ͼ产生兌?br>令函数返回一个常量|往往可以降低因客户错误而造成的意外,而又不至于放弃安全性和高效性。例如,考虑有理数的operator*声明式:
class Rational(){...};
const Rational operator* (const Rational & lhs, const Rational & rhs);
也许你会说ؓ什么返回一个const对象Q原因是如果不这样别人可能实现这L暴行Q?br>Rational a,b,c;
...
(a*b)=c;
下面Q主要说明const作用于成员函数?br>许多人都忽视了这么一个事实,那就是如果两个成员函数只是常量性不同,那么他们是可以重载的。考虑以下q个用来表示一大块文字的classQ?br>

class TextBlock{
public:

const char& operator[](std::size_t position) const
{return text[position];}
char& operator[](std::size_t position)
{return text[position];}
private:
std::
string text;
}
;
TextBlock的operator[]可以q么使用Q?br>TextBlock tb(
"Hello");
std::cout 
<< tb[0];  //调用non-const 

const TextBlock ctb("Hello");
std::cont 
<< ctb[0]; //调用const

真是情Ş中const对象多用于passed by pointer-to-const或passed by reference-to-const的传递结果。上q的ctb太过于造作Q下边这个比较真实:
void print (const TextBlocd& ctb)
{
  std::cout 
<< ctb[0];
  
}

只用重蝲operator[]q对不同的版本给予不同的q回cdQ就可以令const和non-const获得不同的处理?br>此处需要注意一点,non-const operator[]的返回类型是个reference to char,不是char。如果operator[]q回的是个charQ下边的赋值就不能通过~译Q?br>tb[0] = 'x'; //error c2106: ' = ' : left operand must be l-value
那是因ؓQ如果函数的q回cd是个内置cdQ那么改动函数的q回g来就不合法。纵使合法,C++以by valueq回对象q一事实Q条?0Q意味着改动的其实只是tb.text[0]的一个副本,不是tb.text[0]本nQ那不是我们惌的结果?br>下边来说说在const和non-const成员函数中避免重?br>假设TextBlockQ和CTextBlockQ内的operator[]不单只是q回一个reference指向某字W,也执行边界检查、志记访问信息、甚臛_能进行数据完整性检验。把所有这些同时放qconst和non-const operator[]中,Dq样的一个怪物Q?br>
class TextBlock{
public:

const char& operator[](std::size_t position) const
{
      
//边界?bounds checking)
      //志记数据讉K(log access data)
      //验数据完整?verify data integrity)
return text[position];
}

char& operator[](std::size_t position)
{
      
//边界?bounds checking)
      //志记数据讉K(log access data)
      //验数据完整?verify data integrity)
return text[position];
}

private:
std::
string text;
}
;
其中代码的代码重复性及伴随的编译时_l护Q代码膨胀{问题真是o人头疼啊。当然了Q将边界?#8230;…{所有代码移植到另一个成员函敎ͼqo两个版本的operator[]调用它,是可能的Q但是还是重复了一些代码,例如函数调用Q两ơreturn语句{?br>我们真正要做的,是实现operator[]的机能一ơƈ使用它两ơ。也是_你必M一个调用另一个。这促我们常量性{除(casting away constnessQ?br>׃般而言Qcasting是一个糟p的xQ在条款27中有详细的说明。然而代码重复也不是什么o人愉快的l验。本例中cosnt operator[]完全做掉了non-const版本该做的一切,唯一不同是其q回cd多了一个const资格修饰。这U情况下如果返回值的const转除是安全的Q因Z调用non-const operator[]都一定首先有个non-const对象Q否则就不能够调用non-const函数。所以onon-const operator[]调用其const兄弟是一个避免重复的安全做法Q?br>
class TextBlock{
public:

const char& operator[](std::size_t position) const
{



return text[position];
}

char& operator[](std::size_t position)
{
const_cast
<char&>(static_cast<const TextBlock&>
(
*this)[position]);
}


}
;
q里面有两个转型动作Q而不是一个。我们打让non-const operator[]调用const兄弟Q但是non-const如果只是单纯调用operator[]Q会递归调用自己。ؓ了避免无I递归Q我们必L指用的是const operator[]。因此,q里?this从其原始cdTextBlock&转型为const TextBlock&。所以这里有两次转型Q第一ơ用来ؓ*thisdconstQ第二次则是从const operator[]的返回gU除const。添加const的那一ơ{型强q进行了一ơ安全{型,所以采用static_cast。移除const的那个动作只能由const_cast完成Q没有其他选择?br>下面来考虑一下反向的做法Qoconst来调用non-const以避免重复。这个不是我们应该做的。const成员函数承诺l对不改变其对象的逻辑状态,non-const成员函数却没有这般承诺。如果在const函数内部调用了non-const函数Q就是冒了这L风险Q你曄承诺不改动的那个对象被改动了。这是Z?#8220;const成员函数调用non-const成员函数”是一U错误行为:因ؓ对象有可能因此而被改动。反向调用才是安全的Qnon-const函数本来可以对其对象做M动作Q所以在其中调用一个const成员函数q不会带来Q何风险?br>
本条目ȝQ?br>

Things to Remember

  • Declaring something const helps compilers detect usage errors. const can be applied to objects at any scope, to function parameters and return types, and to member functions as a whole.

  • Compilers enforce bitwise constness, but you should program using conceptual constness.

  • When const and non-const member functions have essentially identical implementations, code duplication can be avoided by having the non-const version call the const version.



Edmund 2008-12-09 23:00 发表评论
]]>
effectiv c++ MW记之一http://www.shnenglu.com/xiaohe0817/archive/2008/12/08/68891.htmlEdmundEdmundMon, 08 Dec 2008 15:50:00 GMThttp://www.shnenglu.com/xiaohe0817/archive/2008/12/08/68891.htmlhttp://www.shnenglu.com/xiaohe0817/comments/68891.htmlhttp://www.shnenglu.com/xiaohe0817/archive/2008/12/08/68891.html#Feedback0http://www.shnenglu.com/xiaohe0817/comments/commentRss/68891.htmlhttp://www.shnenglu.com/xiaohe0817/services/trackbacks/68891.html现在在维护代码的时候,前辈们大片大片的宏搞得我是那个晕头{向啊Q真希望他们也看q本条款?br>1.CaseQ?define ASPECT_RATIO 1.653
   RecommendationQconst double AspectRatio = 1.653Q?br>Reason:   当用ASPECT_RATIO但是获得一个编译错误信息时Q可能你会很是发冏,因ؓq个错误信息也许会提?.653而不是ASPECT_RATIO。如果ASPECT_RATIO定义在非你所写的头文件中Q你更是因ؓq踪他而浪Ҏ间。改为推荐的方式后,你找到的肯定是AspectRatio。当以常量替?defineӞ有两U注意的情况Q第一U是定义帔R指针Qconst pointersQ。由于常量定义式常放在头文g内,因此有必要将指针也声明ؓconst。例如在一个头文g内定义一个常量的char*-based字符Ԍ必须写const两次Q?br>const char* const authorName = "Edmund";
q里采用string对象比其前辈char*-based更合适,
const std::string authorName("Edmund");
W二U是class专属帔R。ؓ了将帔R的作用域限制在class内,你必让他成为class的一个成员;而ؓ保此常量只有一个实体,则必d明ؓstaticQ?br>class GamePlayer{
private:
static const int NumTurns = 5;
int scores[NumTurns];
...
}
然而,你看到的是NumTurns的声明式而不是定义式QC++通常要求我们所使用的Q何东襉K要有一个定义式Q但如果他是个class的专属常量而又是static且ؓ整数cdQintsQcharsQboolsQ,则做Ҏ处理。只要不取他们的地址Q你可以声明q用他们而无需提供定义式。但如果取某个class专属帔R的地址Q或U不取地址而编译器却坚持要看到一个定义式Q你必L供另外一个定义式Q?br>const int GamePlayer::NumTurns;
׃NumTurns在声明时已经获得了初|因此定义时不可以再设初倹{此外,Ҏ谓的“in-class初D?#8221;也只允许Ҏ数常量进行。如果ؓ非整型则可以采用下面的这U方式:
class CostEstimate{
private:
static const double FudgeFactor;
...
}

const double CostEstimate::FudgeFactor = 1.35;
当你在编译期需要一个class帔R|例如在上qGamePlayer::scores的数l声明中Q此时如果编译器不允?#8220;static整数型class帔R”完成“in-class初D?#8221;Q可采用enum来解冻I其理论基?#8220;一个属于枚丄型的数值可权充ints被?#8221;Q于是GamePlayer可定义如下:
class GamePlayer{
private:
enum{NumTurns = 5};
int scores[NumTurns];
...
};
注意Q取一个const的值是合法的,但是取一个enum的值就是不合法的,取一?define的g是不合法的。如果你不想让别得一个pointer或者reference指向你的某个整数帔RQenum可以帮助你实现这个约束?br>下边l箋说预处理器。另外一个常见的#define误用的情景是以他来实现宏Q宏看v来像函数Q但是不会招致函数调用带来的额外开销Q例如:
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))。他的缺点就不说了,替代方式Q?br>template<class T> inline void callWithMax(const T& a, const T& b)
{
f(a > b?a : b);
}
本条目ȝQ?br>1.对于单纯帔RQ最好以const对象或者enums替换#definesQ?br>2.对于形似函数的宏Q最好改用inline函数替换#defines?br>



PsQ本文是W一ơ在cppblog上发表的文章Q呵c很早就惛_q上面写点了Q但是不是忙q就是忙那,昨天下定军_Q先把effective C++(3e)里面?5条读书笔记写在这上面。打每天一个条目,q里面好多跟书上的句子一P但是全是我自己敲q去的,不存在Q何的paste。所写均是自己搞清楚的,不明白地方的暂时没有d?br>


Edmund 2008-12-08 23:50 发表评论
]]>
99þһa| þõӰ2021| һõþۺϺݺݰ| 99þҹɫƷվ| þۺϾɫۺվ| 91þþƷ91ɫҲ| ŷպƷþþѹۿ| һŮȫƾþƬ| ŮþþŮ| ƷžžþþƷŮͬŷպۺ | þ޾Ʒa| ݺɫþþۺϲ| ޹Ʒľþþ| þˮav뾫Ʒ鶹| ˾Ʒþ޸岻 | þֹۺ޾Ʒ| þ99Ʒһ | þþAVɫۺ| þһٸ| 69þþƷһ| þþþþþþ| һۺϾþ| þþƷ99þ㽶ɫ| ŷպۺϾþ| ձƬҹþ| þùѾƷ| ҹƷþþĸ | þҹӰ| ˾Ʒþ޸岻 | ƷþþþӰӲ| Ļþۺ| ޾Ʒþþþþ| þþþӰԺŮ| ݺɫþþһ| 97Ʒ˾þô߽| þþþAVۺϲҰ| 㽶þҹɫƷС˵| ŷ㽶þۺվ| 99þùѸ| þþùҺ| ȾþùŷһƷ|