﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-ZGK.C++.INFO</title><link>http://www.cppblog.com/info/</link><description /><language>zh-cn</language><lastBuildDate>Tue, 14 Apr 2026 23:06:09 GMT</lastBuildDate><pubDate>Tue, 14 Apr 2026 23:06:09 GMT</pubDate><ttl>60</ttl><item><title>无题</title><link>http://www.cppblog.com/info/archive/2005/12/11/1675.html</link><dc:creator>zgk.info</dc:creator><author>zgk.info</author><pubDate>Sun, 11 Dec 2005 08:54:00 GMT</pubDate><guid>http://www.cppblog.com/info/archive/2005/12/11/1675.html</guid><wfw:comment>http://www.cppblog.com/info/comments/1675.html</wfw:comment><comments>http://www.cppblog.com/info/archive/2005/12/11/1675.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/info/comments/commentRss/1675.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/info/services/trackbacks/1675.html</trackback:ping><description><![CDATA[<P>对于这个blog,我觉得没有必要做什么特别的说明;<BR>目的也很明确,就是为了更好的学习C++!<BR><BR></P><img src ="http://www.cppblog.com/info/aggbug/1675.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/info/" target="_blank">zgk.info</a> 2005-12-11 16:54 <a href="http://www.cppblog.com/info/archive/2005/12/11/1675.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++箴言：谨慎使用多继承</title><link>http://www.cppblog.com/info/archive/2005/12/11/1674.html</link><dc:creator>zgk.info</dc:creator><author>zgk.info</author><pubDate>Sun, 11 Dec 2005 08:30:00 GMT</pubDate><guid>http://www.cppblog.com/info/archive/2005/12/11/1674.html</guid><wfw:comment>http://www.cppblog.com/info/comments/1674.html</wfw:comment><comments>http://www.cppblog.com/info/archive/2005/12/11/1674.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/info/comments/commentRss/1674.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/info/services/trackbacks/1674.html</trackback:ping><description><![CDATA[<FONT size=2>　　触及 multiple inheritance (MI)（多继承）的时候，C++ 社区就会鲜明地分裂为两个基本的阵营。一个阵营认为如果 single inheritance (SI)（单继承）是有好处的，multiple inheritance（多继承）一定更有好处。另一个阵营认为 single inheritance（单继承）有好处，但是多继承引起的麻烦使它得不偿失。在本文中，我们的主要目的是理解在 MI 问题上的这两种看法。 <BR><BR>　　首要的事情之一是要承认当将 MI 引入设计领域时，就有可能从多于一个的 base class（基类）中继承相同的名字（例如，函数，typedef，等等）。这就为歧义性提供了新的时机。例如：<BR><BR>class BorrowableItem { // something a library lets you borrow<BR>public:<BR>　void checkOut(); // check the item out from the library<BR>　..<BR>};<BR><BR>class ElectronicGadget {<BR>private:<BR>　bool checkOut() const; // perform self-test, return whether<BR>　... // test succeeds<BR>};<BR><BR>class MP3Player: // note MI here<BR>public BorrowableItem, // (some libraries loan MP3 players)<BR>public ElectronicGadget<BR>{ ... }; // class definition is unimportant<BR><BR>MP3Player mp;<BR><BR>mp.checkOut(); // ambiguous! which checkOut? <BR><BR>　　注意这个例子，即使两个函数中只有一个是可访问的，对 checkOut 的调用也是有歧义的。（checkOut 在 BorrowableItem 中是 public（公有）的，但在 ElectronicGadget 中是 private（私有）的。）这与 C++ 解析 overloaded functions（重载函数）调用的规则是一致的：在看到一个函数的是否可访问之前，C++ 首先确定与调用匹配最好的那个函数。只有在确定了 best-match function（最佳匹配函数）之后，才检查可访问性。这目前的情况下，两个 checkOuts 具有相同的匹配程度，所以就不存在最佳匹配。因此永远也不会检查到 ElectronicGadget::checkOut 的可访问性。<BR><BR>　　为了消除歧义性，你必须指定哪一个 base class（基类）的函数被调用：<BR><BR>mp.BorrowableItem::checkOut(); // ah, that checkOut... <BR><BR>　　当然，你也可以尝试显式调用 ElectronicGadget::checkOut，但这样做会有一个 "you're trying to call a private member function"（你试图调用一个私有成员函数）错误代替歧义性错误。<BR><BR>　　multiple inheritance（多继承）仅仅意味着从多于一个的 base class（基类）继承，但是在还有 higher-level base classes（更高层次基类）的 hierarchies（继承体系）中出现 MI 也并不罕见。这会导致有时被称为 "deadly MI diamond"（致命的多继承菱形）的后果。<BR><BR>class File { ... };<BR>class InputFile: public File { ... };<BR>class OutputFile: public File { ... };<BR>class IOFile: public InputFile,<BR>public OutputFile<BR>{ ... }; <BR><BR>&nbsp;&nbsp;<BR><BR>　　在一个“在一个 base class（基类）和一个 derived class（派生类）之间有多于一条路径的 inheritance hierarchy（继承体系）”（就像上面在 File 和 IOFile 之间，有通过 InputFile 和 OutputFile 的两条路径）的任何时候，你都必须面对是否需要为每一条路径复制 base class（基类）中的 data members（数据成员）的问题。例如，假设 File class 有一个 data members（数据成员）fileName。IOFile 中应该有这个 field（字段）的多少个拷贝呢？一方面，它从它的每一个 base classes（基类）继承一个拷贝，这就暗示 IOFile 应该有两个 fileName data members（数据成员）。另一方面，简单的逻辑告诉我们一个 IOFile object（对象）应该仅有一个 file name（文件名），所以通过它的两个 base classes（基类）继承来的 fileName field（字段）不应该被复制。<BR><BR>　　C++ 在这个争议上没有自己的立场。它恰当地支持两种选项，虽然它的缺省方式是执行复制。如果那不是你想要的，你必须让这个 class（类）带有一个 virtual base class（虚拟基类）的数据（也就是 File）。为了做到这一点，你要让从它直接继承的所有的 classes（类）使用 virtual inheritance（虚拟继承）：<BR><BR>class File { ... };<BR>class InputFile: virtual public File { ... };<BR>class OutputFile: virtual public File { ... };<BR>class IOFile: public InputFile,<BR>public OutputFile<BR>{ ... }; <BR><BR>　　标准 C++ 库包含一个和此类似的 MI hierarchy（继承体系），只是那个 classes（类）是 class templates（类模板），名字是 basic_ios，basic_istream，basic_ostream 和 basic_iostream，而不是 File，InputFile，OutputFile 和 IOFile。<BR><BR>　　从正确行为的观点看，public inheritance（公有继承）应该总是 virtual（虚拟）的。如果这是唯一的观点，规则就变得简单了：你使用 public inheritance（公有继承）的任何时候，都使用 virtual public inheritance（虚拟公有继承）。唉，正确性不是唯一的视角。避免 inherited fields（继承来的字段）复制需要在编译器的一部分做一些 behind-the-scenes legerdemain（幕后的戏法），而结果是从使用 virtual inheritance（虚拟继承）的 classes（类）创建的 objects（对象）通常比不使用 virtual inheritance（虚拟继承）的要大。访问 virtual base classes（虚拟基类）中的 data members（数据成员）也比那些 non-virtual base classes（非虚拟基类）中的要慢。编译器与编译器之间有一些细节不同，但基本的要点很清楚：virtual inheritance costs（虚拟继承要付出成本）。<BR><BR>　　它也有一些其它方面的成本。支配 initialization of virtual base classes（虚拟基类初始化）的规则比 non-virtual bases（非虚拟基类）的更加复杂而且更不直观。初始化一个 virtual base（虚拟基）的职责由 hierarchy（继承体系）中 most derived class（层次最低的派生类）承担。这个规则中包括的含义：<BR><BR>　　（1） 从需要 initialization（初始化）的 virtual bases（虚拟基）派生的 classes（类）必须知道它们的 virtual bases（虚拟基），无论它距离那个 bases（基）有多远；<BR><BR>　　（2） 当一个新的 derived class（派生类）被加入继承体系时，它必须为它的 virtual bases（虚拟基）（包括直接的和间接的）承担 initialization responsibilities（初始化职责）。<BR><BR>　　我对于 virtual base classes（虚拟基类）（也就是 virtual inheritance（虚拟继承））的建议很简单。首先，除非必需，否则不要使用 virtual bases（虚拟基）。缺省情况下，使用 non-virtual inheritance（非虚拟继承）。第二，如果你必须使用 virtual base classes（虚拟基类），试着避免在其中放置数据。这样你就不必在意它的 initialization（初始化）（以及它的 turns out（清空），assignment（赋值））规则中的一些怪癖。值得一提的是 Java 和 .NET 中的 Interfaces（接口）不允许包含任何数据，它们在很多方面可以和 C++ 中的 virtual base classes（虚拟基类）相比照。<BR><BR>　　现在我们使用下面的 C++ Interface class（接口类）（参见《C++箴言：最小化文件之间的编译依赖》）来为 persons（人）建模：<BR><BR>class IPerson {<BR>public:<BR>　virtual ~IPerson();<BR><BR>　virtual std::string name() const = 0;<BR>　virtual std::string birthDate() const = 0;<BR>}; <BR><BR>　　IPerson 的客户只能使用 IPerson 的 pointers（指针）和 references（引用）进行编程，因为 abstract classes（抽象类）不能被实例化。为了创建能被当作 IPerson objects（对象）使用的 objects（对象），IPerson 的客户使用 factory functions（工厂函数）（再次参见 Item 31）instantiate（实例化）从 IPerson 派生的 concrete classes（具体类）：<BR><BR>// factory function to create a Person object from a unique database ID;<BR>// see Item 18 for why the return type isn't a raw pointer<BR>std::tr1::shared_ptr&lt;IPerson&gt; makePerson(DatabaseID personIdentifier);<BR><BR>// function to get a database ID from the user<BR>DatabaseID askUserForDatabaseID();<BR><BR><BR>DatabaseID id(askUserForDatabaseID());<BR>std::tr1::shared_ptr&lt;IPerson&gt; pp(makePerson(id)); // create an object<BR>// supporting the<BR>// IPerson interface<BR><BR>... // manipulate *pp via<BR>// IPerson's member<BR>// functions <BR><BR>　　但是 makePerson 怎样创建它返回的 pointers（指针）所指向的 objects（对象）呢？显然，必须有一些 makePerson 可以实例化的从 IPerson 派生的 concrete class（具体类）。<BR><BR>　　假设这个 class（类）叫做 CPerson。作为一个 concrete class（具体类），CPerson 必须提供它从 IPerson 继承来的 pure virtual functions（纯虚拟函数）的 implementations（实现）。它可以从头开始写，但利用包含大多数或全部必需品的现有组件更好一些。例如，假设一个老式的 database-specific class（老式的数据库专用类）PersonInfo 提供了 CPerson 所需要的基本要素：<BR><BR>class PersonInfo {<BR>public:<BR>　explicit PersonInfo(DatabaseID pid);<BR>　virtual ~PersonInfo();<BR><BR>　virtual const char * theName() const;<BR>　virtual const char * theBirthDate() const;<BR>　...<BR><BR>private:<BR>　virtual const char * valueDelimOpen() const; // see<BR>　virtual const char * valueDelimClose() const; // below<BR>　...<BR>}; <BR><BR>　　你可以看出这是一个老式的 class（类），因为 member functions（成员函数）返回 const char*s 而不是 string objects（对象）。尽管如此，如果鞋子合适，为什么不穿呢？这个 class（类）的 member functions（成员函数）的名字暗示结果很可能会非常合适。<BR><BR>　　你突然发现 PersonInfo 是设计用来帮助以不同的格式打印 database fields（数据库字段）的，每一个字段的值的开始和结尾通过指定的字符串定界。缺省情况下，字段值开始和结尾定界符是方括号，所以字段值 "Ring-tailed Lemur" 很可能被安排成这种格式：<BR><BR>[Ring-tailed Lemur] <BR><BR>　　根据方括号并非满足 PersonInfo 的全体客户的期望的事实，virtual functions（虚拟函数）valueDelimOpen 和 valueDelimClose 允许 derived classes（派生类）指定它们自己的开始和结尾定界字符串。PersonInfo 的 member functions（成员函数）的 implementations（实现）调用这些 virtual functions（虚拟函数）在它们返回的值上加上适当的定界符。作为一个例子使用 PersonInfo::theName，代码如下：<BR><BR>const char * PersonInfo::valueDelimOpen() const<BR>{<BR>　return "["; // default opening delimiter<BR>}<BR><BR>const char * PersonInfo::valueDelimClose() const<BR>{<BR>　return "]"; // default closing delimiter<BR>}<BR><BR>const char * PersonInfo::theName() const<BR>{<BR>　// reserve buffer for return value; because this is<BR>　// static, it's automatically initialized to all zeros<BR>　static char value[Max_Formatted_Field_Value_Length];<BR><BR>　// write opening delimiter<BR>　std::strcpy(value, valueDelimOpen());<BR><BR>　append to the string in value this object's name field (being careful to avoid buffer overruns!)<BR><BR>　// write closing delimiter<BR>　std::strcat(value, valueDelimClose());<BR><BR>　return value;<BR>} <BR><BR>　　有人可能会质疑 PersonInfo::theName 的陈旧的设计（特别是一个 fixed-size static buffer（固定大小静态缓冲区）的使用，这样的东西发生 overrun（越界）和 threading（线程）问题是比较普遍的——参见《C++箴言：必须返回对象时别返回引用》），但是请把这样的问题放到一边而注意这里：theName 调用 valueDelimOpen 生成它要返回的 string（字符串）的开始定界符，然后它生成名字值本身，然后它调用 valueDelimClose。<BR><BR>　　因为 valueDelimOpen 和 valueDelimClose 是 virtual functions（虚拟函数），theName 返回的结果不仅依赖于 PersonInfo，也依赖于从 PersonInfo 派生的 classes（类）。<BR><BR>　　对于 CPerson 的实现者，这是好消息，因为当细读 IPerson documentation（文档）中的 fine print（晦涩的条文）时，你发现 name 和 birthDate 需要返回未经修饰的值，也就是，不允许有定界符。换句话说，如果一个人的名字叫 Homer，对那个人的 name 函数的一次调用应该返回 "Homer"，而不是 "[Homer]"。<BR><BR>　　CPerson 和 PersonInfo 之间的关系是 PersonInfo 碰巧有一些函数使得 CPerson 更容易实现。这就是全部。因而它们的关系就是 is-implemented-in-terms-of，而我们知道有两种方法可以表现这一点：经由 composition（复合）（参见《C++箴言：通过composition模拟“has-a”》）和经由 private inheritance（私有继承）（参见《C++箴言：谨慎使用私有继承》）。《C++箴言：谨慎使用私有继承》指出 composition（复合）是通常的首选方法，但如果 virtual functions（虚拟函数）要被重定义，inheritance（继承）就是必不可少的。在当前情况下，CPerson 需要重定义 valueDelimOpen 和 valueDelimClose，所以简单的 composition（复合）做不到。最直截了当的解决方案是让 CPerson 从 PersonInfo privately inherit（私有继承），虽然 《C++箴言：谨慎使用私有继承》说过只要多做一点工作，则 CPerson 也能用 composition（复合）和 inheritance（继承）的组合有效地重定义 PersonInfo 的 virtuals（虚拟函数）。这里，我们用 private inheritance（私有继承）。<BR><BR>　　但是 CPerson 还必须实现 IPerson interface（接口），而这被称为 public inheritance（公有继承）。这就引出一个 multiple inheritance（多继承）的合理应用：组合 public inheritance of an interface（一个接口的公有继承）和 private inheritance of an implementation（一个实现的私有继承）：<BR><BR>class IPerson { // this class specifies the<BR>public: // interface to be implemented<BR>　virtual ~IPerson();<BR><BR>　virtual std::string name() const = 0;<BR>　virtual std::string birthDate() const = 0;<BR>};<BR><BR>class DatabaseID { ... }; // used below; details are<BR>// unimportant<BR><BR>class PersonInfo { // this class has functions<BR>public: // useful in implementing<BR>　explicit PersonInfo(DatabaseID pid); // the IPerson interface<BR>　virtual ~PersonInfo();<BR><BR>　virtual const char * theName() const;<BR>　virtual const char * theBirthDate() const;<BR><BR>　virtual const char * valueDelimOpen() const;<BR>　virtual const char * valueDelimClose() const;<BR>　...<BR>};<BR><BR>class CPerson: public IPerson, private PersonInfo { // note use of MI<BR>public:<BR>　explicit CPerson( DatabaseID pid): PersonInfo(pid) {}<BR>　virtual std::string name() const // implementations<BR>　{ return PersonInfo::theName(); } // of the required<BR>　// IPerson member<BR>　virtual std::string birthDate() const // functions<BR>　{ return PersonInfo::theBirthDate(); }<BR>private: // redefinitions of<BR>　const char * valueDelimOpen() const { return ""; } // inherited virtual<BR>　const char * valueDelimClose() const { return ""; } // delimiter<BR>}; // functions <BR><BR>　　在 UML 中，这个设计看起来像这样：<BR><BR><BR><BR>　　这个例子证明 MI 既是有用的，也是可理解的。<BR><BR>　　时至今日，multiple inheritance（多继承）不过是 object-oriented toolbox（面向对象工具箱）里的又一种工具而已，典型情况下，它的使用和理解更加复杂，所以如果你得到一个或多或少等同于一个 MI 设计的 SI 设计，则 SI 设计总是更加可取。如果你能拿出来的仅有的设计包含 MI，你应该更加用心地考虑一下——总会有一些方法使得 SI 也能做到。但同时，MI 有时是最清晰的，最易于维护的，最合理的完成工作的方法。在这种情况下，毫不畏惧地使用它。只是要确保谨慎地使用它。<BR><BR>　　Things to Remember<BR><BR>　　·multiple inheritance（多继承）比 single inheritance（单继承）更复杂。它能导致新的歧义问题和对 virtual inheritance（虚拟继承）的需要。<BR><BR>　　·virtual inheritance（虚拟继承）增加了 size（大小）和 speed（速度）成本，以及 initialization（初始化）和 assignment（赋值）的复杂度。当 virtual base classes（虚拟基类）没有数据时它是最适用的。<BR><BR>　　·multiple inheritance（多继承）有合理的用途。一种方案涉及组合从一个 Interface class（接口类）的 public inheritance（公有继承）和从一个有助于实现的 class（类）的 private inheritance（私有继承）。 <BR></FONT><img src ="http://www.cppblog.com/info/aggbug/1674.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/info/" target="_blank">zgk.info</a> 2005-12-11 16:30 <a href="http://www.cppblog.com/info/archive/2005/12/11/1674.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++指针使用方法</title><link>http://www.cppblog.com/info/archive/2005/12/11/1673.html</link><dc:creator>zgk.info</dc:creator><author>zgk.info</author><pubDate>Sun, 11 Dec 2005 08:26:00 GMT</pubDate><guid>http://www.cppblog.com/info/archive/2005/12/11/1673.html</guid><wfw:comment>http://www.cppblog.com/info/comments/1673.html</wfw:comment><comments>http://www.cppblog.com/info/archive/2005/12/11/1673.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/info/comments/commentRss/1673.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/info/services/trackbacks/1673.html</trackback:ping><description><![CDATA[<FONT size=2>在下列函数声明中，为什么要同时使用*和&amp;符号？以及什么场合使用这种声明方式? 　&nbsp;<BR><BR>　　 void func1( MYCLASS *&amp;pBuildingElement );&nbsp;<BR><BR>　　 论坛中经常有人问到这样的问题。本文试图通过一些实际的指针使用经验来解释这个问题。&nbsp;<BR><BR>　　 仔细看一下这种声明方式，确实有点让人迷惑。在某种意义上，"*"和"&amp;"是意思相对的两个东西，把它们放在一起有什么意义呢？。为了理解指针的这种做法，我们先复习一下C/C++编程中无所不在的指针概念。我们都知道MYCLASS*的意思：指向某个对象的指针，此对象的类型为MYCLASS。 void func1(MYCLASS *pMyClass); 　&nbsp;<BR><BR>　　// 例如： MYCLASS* p = new MYCLASS;&nbsp;<BR><BR>　　 func1(p);&nbsp;<BR><BR>　　 上面这段代码的这种处理方法想必谁都用过，创建一个MYCLASS对象，然后将它传入func1函数。现在假设此函数要修改pMyClass： void func1(MYCLASS *pMyClass)&nbsp;<BR><BR>　　 {&nbsp;<BR>　　 DoSomething(pMyClass);&nbsp;<BR>　　 pMyClass = // 其它对象的指针&nbsp;<BR>　　 }　&nbsp;<BR><BR>　　 第二条语句在函数过程中只修改了pMyClass的值。并没有修改调用者的变量p的值。如果p指向某个位于地址0x008a00的对象，当func1返回时，它仍然指向这个特定的对象。（除非func1有bug将堆弄乱了，完全有这种可能。）&nbsp;<BR><BR>　　现在假设你想要在func1中修改p的值。这是你的权利。调用者传入一个指针，然后函数给这个指针赋值。以往一般都是传双指针，即指针的指针，例如，CMyClass**。 MYCLASS* p = NULL;&nbsp;<BR><BR>　　 func1(&amp;p);&nbsp;<BR>　　 void func1(MYCLASS** pMyClass);&nbsp;<BR>　　 {&nbsp;<BR>　　 *pMyClass = new MYCLASS;&nbsp;<BR>　　 ……&nbsp;<BR>　　 }　　&nbsp;<BR><BR>　　调用func1之后，p指向新的对象。在COM编程中，你到处都会碰到这样的用法--例如在查询对象接口的QueryInterface函数中：&nbsp;<BR>interface ISomeInterface {&nbsp;<BR>　　 HRESULT QueryInterface(IID &amp;iid, void** ppvObj);&nbsp;<BR>　　 ……&nbsp;<BR>　　 };&nbsp;<BR>　　 LPSOMEINTERFACE p=NULL;&nbsp;<BR>　　 pOb-&gt;QueryInterface(IID_SOMEINTERFACE, &amp;p);　　&nbsp;<BR><BR>　　 此处，p是SOMEINTERFACE类型的指针，所以&amp;p便是指针的指针，在QueryInterface返回的时候，如果调用成功，则变量p包含一个指向新的接口的指针。&nbsp;<BR><BR>　　 如果你理解指针的指针，那么你肯定就理解指针引用，因为它们完全是一回事。如果你象下面这样声明函数：&nbsp;<BR><BR>　　 void func1(MYCLASS *&amp;pMyClass);&nbsp;<BR>　　 {&nbsp;<BR>　　 pMyClass = new MYCLASS;&nbsp;<BR>　　 ……&nbsp;<BR>　　 }　　&nbsp;<BR><BR>　　 其实，它和前面所讲得指针的指针例子是宦胧拢皇怯锓ㄓ兴煌４莸氖焙虿挥么玴的地址&amp;p，而是直接传p本身：　　&nbsp;<BR><BR>　　 MYCLASS* p = NULL;&nbsp;<BR>　　 func1(p);　　&nbsp;<BR><BR>　　 在调用之后，p指向一个新的对象。一般来讲，引用的原理或多或少就象一个指针，从语法上看它就是一个普通变量。所以只要你碰到*&amp;，就应该想到**。也就是说这个函数修改或可能修改调用者的指针，而调用者象普通变量一样传递这个指针，不使用地址操作符&amp;。&nbsp;<BR><BR>　　 至于说什么场合要使用这种方法，我会说，极少。MFC在其集合类中用到了它--例如，CObList，它是一个CObjects指针列表。&nbsp;<BR>class CObList : public CObject {&nbsp;<BR>　　 ……&nbsp;<BR>　　 // 获取/修改指定位置的元素&nbsp;<BR>　　 CObject*&amp; GetAt(POSITION position);&nbsp;<BR>　　 CObject* GetAt(POSITION position) const;&nbsp;<BR>　　 };　　&nbsp;<BR><BR>　　这里有两个GetAt函数，功能都是获取给定位置的元素。区别何在呢？&nbsp;<BR><BR>　　区别在于一个让你修改列表中的对象，另一个则不行。所以如果你写成下面这样： CObject* pObj = mylist.GetAt(pos);　　&nbsp;<BR><BR>　　则pObj是列表中某个对象的指针，如果接着改变pObj的值： pObj = pSomeOtherObj;&nbsp;<BR><BR>　　这并改变不了在位置pos处的对象地址，而仅仅是改变了变量pObj。但是，如果你写成下面这样： CObject*&amp; rpObj = mylist.GetAt(pos);　　&nbsp;<BR><BR>　　 现在，rpObj是引用一个列表中的对象的指针，所以当改变rpObj时，也会改变列表中位置pos处的对象地址--换句话说，替代了这个对象。这就是为什么CObList会有两个GetAt函数的缘故。一个可以修改指针的值，另一个则不能。注意我在此说的是指针，不是对象本身。这两个函数都可以修改对象，但只有*&amp;版本可以替代对象。&nbsp;<BR></FONT><img src ="http://www.cppblog.com/info/aggbug/1673.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/info/" target="_blank">zgk.info</a> 2005-12-11 16:26 <a href="http://www.cppblog.com/info/archive/2005/12/11/1673.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>