| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 6322 人关注过本帖
标题:推荐:C++语言常见问题解答
取消只看楼主 加入收藏
yuyunliuhen
Rank: 6Rank: 6
等 级:贵宾
威 望:20
帖 子:1435
专家分:0
注 册:2005-12-12
收藏
得分:0 
解构子(destructor)是做什麽的? 
 
解构子乃物件之葬礼。
解构子是用来释放该物件所配置到的资源,譬如:Lock 类别可能会锁住一个 
semaphore,解构子则用来释放它。最常见的例子是:当建构子用了 "new" 以後,解 
构子用 "delete"。 
 
解构子是个「去死吧」的运作行为(method),通常缩写为 "dtor"。 
 

Go confidently in the  directions of your dreams,live the life you have imagined!Just do it!
It is no use learning without thinking!
2006-12-29 22:47
yuyunliuhen
Rank: 6Rank: 6
等 级:贵宾
威 望:20
帖 子:1435
专家分:0
注 册:2005-12-12
收藏
得分:0 
运算子多载(operator overloading)是做什麽的? 
 
它可让使用类别的人以直觉来操作之。 
 
运算子多载让 C/C++ 的运算子,能对自订的型态(物件类别)赋予自订的意义。它 
们形同是函数呼叫的语法糖衣 (syntactic sugar): 
 
        class Fred { 
        public: 
          //... 
        }; 
 
        #if 0 
          Fred add(Fred, Fred);         //没有运算子多载 
          Fred mul(Fred, Fred); 
        #else 
          Fred operator+(Fred, Fred);   //有运算子多载 
          Fred operator*(Fred, Fred); 
        #endif 
 
        Fred f(Fred a, Fred b, Fred c) 
        { 
          #if 0 
            return add(add(mul(a,b), mul(b,c)), mul(c,a));  //没有... 
          #else 
            return a*b + b*c + c*a;                         //有... 
          #endif 
        } 

Go confidently in the  directions of your dreams,live the life you have imagined!Just do it!
It is no use learning without thinking!
2006-12-29 22:47
yuyunliuhen
Rank: 6Rank: 6
等 级:贵宾
威 望:20
帖 子:1435
专家分:0
注 册:2005-12-12
收藏
得分:0 
哪些运算子可以/不能被多载? 
 
大部份都可以被多载。 
不能的 C 运算子有 "." 和 "?:"(和以技术上来说,可算是运算子的 "sizeof")。 
C++ 增加了些自己的运算子,其中除了 "::" 和 ".*". 之外都可以被多载。 
 
底下是个足标(subscript)运算子的例子(它会传回一个参考)。最前面是“不用 ”多载的: 
 
        class Array { 
        public: 
          #if 0 
            int& elem(unsigned i) { if (i>99) error(); return data[i]; } 
          #else 
            int& operator[] (unsigned i) { if (i>99) error(); return data[i]; } 
          #endif 
        private: 
          int data[100]; 
        }; 
 
        main() 
        { 
          Array a; 
 
          #if 0 
            a.elem(10) = 42; 
            a.elem(12) += a.elem(13); 
          #else 
            a[10] = 42; 
            a[12] += a[13]; 
          #endif 
        } 

Go confidently in the  directions of your dreams,live the life you have imagined!Just do it!
It is no use learning without thinking!
2006-12-29 22:48
yuyunliuhen
Rank: 6Rank: 6
等 级:贵宾
威 望:20
帖 子:1435
专家分:0
注 册:2005-12-12
收藏
得分:0 
怎样做一个 "**"「次方」运算子? 
 
无解。 
 
运算子的名称、优先序、结合律以及元数(arity)都被语言所定死了。C++ 里没有 
"**" 运算子,所以你无法替类别订做一个它。 
 
还怀疑的话,考虑看看 "x ** y" 和 "x * (*y)",这两者是完全一样的(换句话说 
,编译器会假设 "y" 是个指标)。此外,运算子多载只是函数呼叫的语法糖衣而已 
,虽然甜甜的,但本质上并未增加什麽东西。我建议你多载 "pow(base,exponent)" 
这个函数(它的倍精确度版本在  中)。 
 
附带一提:operator^ 可以用,但它的优先序及结合律不符「次方」所需。

Go confidently in the  directions of your dreams,live the life you have imagined!Just do it!
It is no use learning without thinking!
2006-12-29 22:48
yuyunliuhen
Rank: 6Rank: 6
等 级:贵宾
威 望:20
帖 子:1435
专家分:0
注 册:2005-12-12
收藏
得分:0 
夥伴(friend)是什麽? 
 
让别的类别或函数能存取到你的类别内部的东西。 
夥伴可以是函数或其他类别。类别会对它的夥伴开放存取权限。正常情况下,程式员 
会下意识、技术性地控制该类别的夥伴与运作行为(否则当你想更动类别时,还得先 
有其他部份的拥有者之同意才行)。 
「夥伴」违反了封装性吗? 
 
若善用之,反而会「强化」封装性。 
 
我们经常得将一个类别切成两半,当这两半各有不同的案例个数及生命期时。在此情 
形之下,它们通常需要直接存取对方的内部(这两半“本来”是在同一个类别里面, 
所以你并未“增加”存取资料结构的运作行为个数;你只是在“搬动”这些运作行为 
所在之处而已)。最安全的实作方式,就是让这两半互为彼此的「夥伴」。 
 
若你如上述般的使用夥伴,你依然是将私有的东西保持在私有的状态。遇到上述的情 
况,如果还呆呆的想避免使用夥伴关系,许多人不是采用公共资料(糟透了!),就 
是弄个公共的 get/set 存取运作行为来存取彼此的资料,事实上这些都破坏了封装 
性。只有在类别的外面该私有资料「仍有其意义」(以使用者的角度来看)时,开放 
出私有资料的存取运作行为才称得上是恰当的做法。多数情况下,「存取运作行为」 
就和「公共资料」一样糟糕:它们对私有资料成员只隐其“名”而已,却未隐藏其“ 
存在”。 
 
同样的,如果将「夥伴函数」做为另一种类别公共存取函数的语法,那就和违反封装 
性的成员函数一样破坏了封装。换句话说,物件类别的夥伴及成员都是「封装的界线 
」,如同「类别定义」本身一样。 
夥伴函数的优缺点? 
 
它提供了某种介面设计上的自由。 
 
成员函数和夥伴函数都有同等的存取特权(100% 的权利),主要的差别在於:夥伴 
函数用起来像是 "f(x)",而成员函数则是 "x.f()"。因此,夥伴函数可让物件类别 
设计者挑选他看得最顺眼的语法,以降低维护成本。 
 
夥伴函数主要的缺点在於:当你想做动态系结(dynamic binding)时,它需要额外 
的程式码。想做出「虚拟夥伴」的效果,该夥伴函数应该呼叫个隐藏的(通常是放在 
"protected:" 里)虚拟成员函数;像这个样子:"void f(Base& b) { b.do_f(); }" 
。衍生类别会覆盖(override)掉那个隐藏的成员函数("void Derived::do_f()") 
,而不是该夥伴函数。 
「夥伴关系无继承及递移性」是什麽意思? 
 
夥伴关系的特权性无法被继承下来:夥伴的衍生类别不必然还是夥伴(我把你当朋友,但这不代表我也一定会信任你的孩子)。如果 "Base" 类别宣告了 "f()" 为它的 
夥伴,"f()" 并不会自动对由 "Base" 衍生出来的 "Derived" 类别所多出来的部份 
拥有特殊的存取权力。 
 
夥伴关系的特权无递移性:夥伴类别的夥伴不必然还是原类别的夥伴(朋友的朋友不 
一定也是朋友)。譬如,如果 "Fred" 类别宣告了 "Wilma" 类别为它的夥伴,而且 
"Wilma" 类别宣告了 "f()" 为它的夥伴,则 "f()" 不见得对 "Fred" 有特殊的存取 
权力。 
应该替类别宣告个成员函数,还是夥伴函数? 
 
可能的话,用成员函数;必要时,就用夥伴。 
 
有时在语法上来看,夥伴比较好(譬如:在 "Fred" 类别中,夥伴函数可把 "Fred" 
弄成是第二个参数,但在成员函数中则一定得放在第一个)。另一个好例子是:二元 
中序式算数运算子(譬如:"aComplex + aComplex" 可能应该定义成夥伴而非成员函 
数,因为你想让 "aFloat + aComplex" 这种写法也能成立;回想一下:成员函数无 
法提升它左侧的参数,因为那样会把引发该成员函数的物件所属之类别给改变掉)。 
 
在其他情况下,请选成员函数而不要用夥伴函数。 

Go confidently in the  directions of your dreams,live the life you have imagined!Just do it!
It is no use learning without thinking!
2006-12-29 22:50
yuyunliuhen
Rank: 6Rank: 6
等 级:贵宾
威 望:20
帖 子:1435
专家分:0
注 册:2005-12-12
收藏
得分:0 
该怎样替 "class Fred" 提供输出功能? 
 
用夥伴函数 operator<<: 
 
        class Fred { 
        public: 
          friend ostream& operator<< (ostream& o, const Fred& fred) 
            { return o << fred.i; } 
          //... 
        private: 
          int i;    //只为了说明起见而设的 
        }; 
 
我们用夥伴而不用成员函数,因为 "Fred" 是第二个参数而非第一个。输入的功能亦 
类似,只是要改写成: 
 
        istream& operator>> (istream& i, Fred& fred); 
                                      // ^^^^^------- 不是 "const Fred& fred"! 
 

Go confidently in the  directions of your dreams,live the life you have imagined!Just do it!
It is no use learning without thinking!
2006-12-29 22:50
yuyunliuhen
Rank: 6Rank: 6
等 级:贵宾
威 望:20
帖 子:1435
专家分:0
注 册:2005-12-12
收藏
得分:0 
为什麽我该用  而不是以前的 ? 
 
增加型别安全、减少错误、增进效率、有延展性、提供衍生能力。 
 
Printf 还好,而 scanf 除了容易写错之外也还算可以,然而和 C++ 的 I/O 系统相 
比,它们都有其限制。C++ 的 I/O(用 "<<" 及 ">>" ),和 C( "printf()" 和 
"scanf()" )相比: 
 
 * 型别安全--要做 I/O 的物件,编译器会静态地事先得知其型别,而不是动态地 
   由 "%" 一栏查知。 
 
 * 不易出错--冗馀的资讯会增加错误的机会。C++ 的 I/O 就不需要多馀的 "%"。 
 
 * 更快速--printf 是个小型语言的「解译器」,该语言主要是由 "%" 这种东西 
   构成的;在执行期它用这些栏位来选择正确的格式化方式。C++ 的 I/O 系统则是 
   静态的依各引数真正的型别来挑选副程式,以增进执行效率。 
 
 * 延展性--C++ I/O 机制可在不改动原有程式码的情况下,就加进使用者新设计 
   的型态(能想像如果大家同时把互不相容的 "%" 栏位塞入 printf 和 scanf,会 
   是怎样的混乱场面?!)。 
 
 * 可衍生(subclassable)--ostream 和 istream(C++ 的 FILE* 代替品)都是 
   真正的类别,因此可以被衍生下去。这意味著:你可以让其他自定的东西有著和 
   stream 雷同的外表与行为,但实际上做的却是你想做的特定事情。你自动就重用 
   了数以万计别人(你甚至不认识它们)写好的 I/O 程式码,而他们也不需要知道 
   你所做的「延伸 stream」类别。 

Go confidently in the  directions of your dreams,live the life you have imagined!Just do it!
It is no use learning without thinking!
2006-12-29 22:51
yuyunliuhen
Rank: 6Rank: 6
等 级:贵宾
威 望:20
帖 子:1435
专家分:0
注 册:2005-12-12
收藏
得分:0 
为什麽我处理输入时,会超过档案的结尾? 
 
因为 eof(档案结尾)的状态,是到「将要超过档案结尾的动作」才会被设定。也就 
是说,读档案的最後一个位元组并不会设定 eof 的状态。 
 
【译注】这也是 C 常见的错误。 
 
如果你的程式像这样: 
 
        int i = 0; 
        while (! cin.eof())  { 
          cin >> x; 
          ++i; 
          // work with x 
        } 
 
你的 i 变数就会多了一。 
你真正该做的是这样: 
 
        int i; 
        while (cin >> x)  { ++i; 
          // work with x 
        } 

Go confidently in the  directions of your dreams,live the life you have imagined!Just do it!
It is no use learning without thinking!
2006-12-29 22:51
yuyunliuhen
Rank: 6Rank: 6
等 级:贵宾
威 望:20
帖 子:1435
专家分:0
注 册:2005-12-12
收藏
得分:0 
什麽是「虚拟成员函数」? 
 
虚拟函数可让衍生的类别「取代」原基底类别所提供的运作。只要某物件是衍生出来 
的,就算我们是透过基底物件的指标,而不是以衍生物件的指标来存取该物件,编译 
器仍会确保「取代後」的成员函数被呼叫。这可让基底类别的演算法被衍生者所替换 
,即使我们不知道衍生类别长什麽样子。 
 
注意:衍生的类别亦可“部份”取代(覆盖,override)掉基底的运作行为(如有必 
要,衍生类别的运作行为亦可呼叫它的基底类别版本)

Go confidently in the  directions of your dreams,live the life you have imagined!Just do it!
It is no use learning without thinking!
2007-01-05 22:48
快速回复:推荐:C++语言常见问题解答
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.018808 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved