| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 6322 人关注过本帖
标题:推荐:C++语言常见问题解答
只看楼主 加入收藏
yuyunliuhen
Rank: 6Rank: 6
等 级:贵宾
威 望:20
帖 子:1435
专家分:0
注 册:2005-12-12
收藏
得分:0 
建构子(constructor)是做什麽的? 
 
建构子乃用来从零开始建立物件。 
 
建构子就像个「初始化函数」;它把一堆散乱的位元组成一个活生生的物件。最低限 
度它会初始化内部用到的栏位,也可能会配置所须的资源(记忆体、档案、semaphore 
、socket 等等)。 
 
"ctor" 是建构子 constructor 最常见的缩写。

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:46
yuyunliuhen
Rank: 6Rank: 6
等 级:贵宾
威 望:20
帖 子:1435
专家分:0
注 册:2005-12-12
收藏
得分:0 
怎样才能让建构子呼叫另一个同处一室的建构子? 
 
没有办法。 
 
原因是:如果你呼叫另一个建构子,编译器会初始化一个暂时的区域性物件;但并没 
有初始化“这个”你想要的物件。你可以用预设参数(default parameter),将两 
个建构子合并起来,或是在私有的 "init()" 成员函数中共享它们的程式码。 
 

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:46
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
快速回复:推荐:C++语言常见问题解答
数据加载中...
 
   



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

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