问题:我现在打算看《TC++PL》。可是发现它里面用了很多的stl。我以前学过c,编程一直用c风格。现在我看书很别扭,那些代码看的似懂非懂。关键问题就是看不懂stl那些语言实现。请问我该怎么办?我是不是该去看看有关stl方面的书呀?还有,我原来的c语言风格有必要改吗?谢谢。
回答:改不改风格要看你的想法。你可以一直在C里做程序,当然也就可以坚持C的风格。如果你想学习C++,那么就应该学习在C++里的编程风格,学习在C++里做程序的正确方式。广泛使用STL也是目前人们所提倡的C++编程风格的一部分。在TC++PL里作者也讨论了编程风格的转变问题。STL的基本使用并不难,应该说用起来也是很方便的。当然,如果你想理解其实现,其中牵涉到的问题很多。但这完全可以等到你更熟悉C++之后再说,开始时不必过分关心那些问题。初学时过于注意去了解细节反而会影响人对全局的理解。至于其他STL的书,我觉得可以先看,也可以不必先去看。其实目前出版的STL书籍中,讨论深度超过TC++PL一书的也不多。
问题:vc6.0下编译包含stl库文件的程序,总有很多错误,包括C++PL中的源程序,怎样解决?。
回答:你说的可能不是错误,而是警告信息,说生成的标识符长于nnn字符(大概是256字符),因此在诊断信息中将无法完全表示。在通过#include包含STL的文件之前加一句:
#pragma warning( disable : 4786 )
即可关闭这一警告。(这并非C++语言的问题,却是用VC做本书题目时常见的问题)
问题:page30Line37(空行不计) "在自由存储分配的Stack" 原书此处为:"allocated on free store".从字面上看这样翻译没问题,但是我们都知道:用new分配的内存是在heap(堆)上的。而很少有人会 用"自由存储"来指代"堆"。这里或许不是一个翻译问题,而是看是否有必要让它对国内读者更通俗些。
回答:堆通常指所有动态空间的总和,而自由空间指的是当时存储管理系统掌握的那一部分堆空间。显然,申请动态存储时,new只能在自由空间中分配。自由空间是通行的说法,我们应该了解。
问题:第5.5节(P90)中的例子在C++ Builder 6.0/VC++ 6.0的代码如下:
#include <iostream> #include <string> #include <vector> using namespace std; struct Pair { double val; string name; }; vector<pair> pairs; double& value(const string& s) { for(int i=0; i < pairs.size(); i++) if(s == pairs[i].name) return pairs[i].val; Pair p={0,s}; pairs.push_back(p); return pairs[pairs.size()-1].val; } int main(int argc, char* argv[]) { string buf; while(cin >> buf) value(buf)++; for(vector<pair>::const_iterator p=pairs.begin();p!=pairs.end();++p) cout << p->name << ":" << p->val << "\n"; return 0; } |
这在CBC6及VC6中都不能通过编译。 将Pair p={0,s};一句改成:
Pair p;//={0,s}; p.name = s; p.val = 0;
就能正确编译及运行.
回答: 原书中的这个例子不错。所写的:
Pair p={0,s};
就是定义一个结构变量并给以初始化,标准允许这样做。这一初始化过程中牵涉到对string类成员的初始化,这是一个复杂操作,其中需要调用string类的构造函数。只能说你发现的是BC和VC与标准不符的一个情况,而不是书中错误。
续:谢谢回函,正如上面所言,这是编译器问题.在GNU/GCC(2.91.57,cygwin)下就能正确编译并运行.看来,下次遇到类似的情况时,我要将这三个编译器都试一试才向您报告了.
回答:即使在三个编译器里都有问题,也未必是书上错误,还是应该看有关代码是否复合语言标准。
问题:在P103页:
default: if(isalpha(ch)) { string_value = ch; while(cin.get(ch)&&isalnum(ch)) string_value.push_back(ch); cin.putback(ch) ; return curr_tok = NAME ; } error("bad token"); return curr_tok = PRINT ; |
的程序段里对string类型的变量用了push_back() 操作 string_back() 是对deque, list , vector进行的操作,从而导致编译错误。
回答:对string用push_back没有问题,C++标准库的string类确实有这个操作(另见523页中间)。如果在你的系统上编译出错,只能说明你所用的系统在这一点上不符合标准。
问题:书的203页上半部分程序中, 对Date构造函数的定义中怎么又出现了另外一个Date的对象today, 我想如果在初始化today的过程中,岂不是又要调用其构造函数吗,是否调用其缺省构造函数呢?但类的声明中也没有出现缺省构造函数。
回答:这个类确实有默认构造函数,就是你所说的那个函数。但该函数的调用将依赖于全局量today。这也是作者在这里想提出的问题。因此,这个程序并不好,因为它导致类定义、全局变量和成员函数定义之间的相互缠绕。然而也请注意,如果写了:
class Date { int d, m, y; public: Date(int dd = 0, int mm = 0, int yy = 0); // ... ... }; Date today(2002,9,11); Date::Date(int dd, int mm, int yy) { d = dd ? dd : today.d; m = mm ? mm : today.m; y = yy ? yy : today.y; } |
使用这个类时应该保证:1)定义全局变量 today 时可以看到类定义;2)定义 Date::Date 时可以看到全局变量today的声明;3)创建 today 时不用默认参数;4)创建 today 必须在创建其他可能用到默认值的Date对象之前。如果这些都能保证,这个程序就可以工作。
另外,还有朋友来函说试验书上204页的有关程序时出现错误信息:
unresolved external symbol "private: static class Date Date::default_date"
这就是因为忘记定义静态变量Data::default_date。
问题: (P461)请帮我讲解一下以下代码:
template<class R, class T> mem_fun_t<R,T> mem_fun(R(T::*f)()) { return mem_fun_t<R,T>(f); } void draw_all(list<Shape*>& lsp) { for_each(lsp.begin(),lsp.end(),mem_fun(&Shape::draw)); } |
回答:第一行最后是模板函数 mem_fun 的参数表,其中的参数是 f,其类型是指向类 T 的成员函数的“成员函数指针”;被指成员函数的返回值为R类型,而且被指函数应是无参函数。 在 for_each 算法最后是对 mem_fun 的调用,以 Shape::draw 作为实际参数。这个成员函数符合上述类型要求。
问题:第686页关于 Handle 类对指针的提取/绑定的例子(见最下)中的 else 里边的:
pcount = new int(1);
是什么意思? 既然*pcount!=0,为何要重新开始计数呢?
template<class X> class Handle{ //... X* get_rep() { return rep; } void bind(X* pp) { if (pp != rep) { if (--*pcount == 0) { delete rep; *pcount = 1; } else pcount = new int(1); rep = pp; } } }; |
回答:这里的问题是要将本句柄对象约束到另一个表示对象,最后的 rep == pp; 完成此事。内层条件语句处理引用计数器,其中考虑了在可能情况下重复使用原计数器的问题。如果引用计数减一后计数器为0,那就可以销毁原来的表示对象,并直接使用原有计算器(将其值置为1,因为下面只引用 pp,计数值应该为1。如果减一后非 0,表示有其他句柄引用那个表示对象。这时就需要另分配一个计数器,new int(1) 完成此事。
本页由裘宗燕建立和维护,保留所有权利。
这里的材料可自由地用于个人学习或普通教学活动。其他方式的使用应事先得到作者书面认可。 |