⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 trouble.texi

📁 理解和实践操作系统的一本好书
💻 TEXI
📖 第 1 页 / 共 4 页
字号:
definition (the ISO C++ standard) was only recently completed.  As aresult, your C++ compiler may occasionally surprise you, even when itsbehavior is correct.  This section discusses some areas that frequentlygive rise to questions of this sort.@menu* Static Definitions::  Static member declarations are not definitions* Name lookup::         Name lookup, templates, and accessing members of base classes* Temporaries::         Temporaries may vanish before you expect* Copy Assignment::     Copy Assignment operators copy virtual bases twice@end menu@node Static Definitions@subsection Declare @emph{and} Define Static Members@cindex C++ static data, declaring and defining@cindex static data in C++, declaring and defining@cindex declaring static data in C++@cindex defining static data in C++When a class has static data members, it is not enough to @emph{declare}the static member; you must also @emph{define} it.  For example:@smallexampleclass Foo@{  @dots{}  void method();  static int bar;@};@end smallexampleThis declaration only establishes that the class @code{Foo} has an@code{int} named @code{Foo::bar}, and a member function named@code{Foo::method}.  But you still need to define @emph{both}@code{method} and @code{bar} elsewhere.  According to the ISOstandard, you must supply an initializer in one (and only one) sourcefile, such as:@smallexampleint Foo::bar = 0;@end smallexampleOther C++ compilers may not correctly implement the standard behavior.As a result, when you switch to @command{g++} from one of these compilers,you may discover that a program that appeared to work correctly in factdoes not conform to the standard: @command{g++} reports as undefinedsymbols any static data members that lack definitions.@node Name lookup@subsection Name lookup, templates, and accessing members of base classes@cindex base class members@cindex two-stage name lookup@cindex dependent name lookupThe C++ standard prescribes that all names that are not dependent ontemplate parameters are bound to their present definitions when parsinga template function or class.@footnote{The C++ standard just uses theterm ``dependent'' for names that depend on the type or value oftemplate parameters.  This shorter term will also be used in the rest ofthis section.}  Only names that are dependent are looked up at the pointof instantiation.  For example, consider@smallexample  void foo(double);  struct A @{    template <typename T>    void f () @{      foo (1);        // @r{1}      int i = N;      // @r{2}      T t;      t.bar();        // @r{3}      foo (t);        // @r{4}    @}    static const int N;  @};@end smallexampleHere, the names @code{foo} and @code{N} appear in a context that doesnot depend on the type of @code{T}.  The compiler will thus require thatthey are defined in the context of use in the template, not only beforethe point of instantiation, and will here use @code{::foo(double)} and@code{A::N}, respectively.  In particular, it will convert the integervalue to a @code{double} when passing it to @code{::foo(double)}.Conversely, @code{bar} and the call to @code{foo} in the fourth markedline are used in contexts that do depend on the type of @code{T}, sothey are only looked up at the point of instantiation, and you canprovide declarations for them after declaring the template, but beforeinstantiating it.  In particular, if you instantiate @code{A::f<int>},the last line will call an overloaded @code{::foo(int)} if one wasprovided, even if after the declaration of @code{struct A}.This distinction between lookup of dependent and non-dependent names iscalled two-stage (or dependent) name lookup.  G++ implements itsince version 3.4.Two-stage name lookup sometimes leads to situations with behaviordifferent from non-template codes.  The most common is probably this:@smallexample  template <typename T> struct Base @{    int i;  @};  template <typename T> struct Derived : public Base<T> @{    int get_i() @{ return i; @}  @};@end smallexampleIn @code{get_i()}, @code{i} is not used in a dependent context, so thecompiler will look for a name declared at the enclosing namespace scope(which is the global scope here).  It will not look into the base class,since that is dependent and you may declare specializations of@code{Base} even after declaring @code{Derived}, so the compiler can'treally know what @code{i} would refer to.  If there is no globalvariable @code{i}, then you will get an error message.In order to make it clear that you want the member of the base class,you need to defer lookup until instantiation time, at which the baseclass is known.  For this, you need to access @code{i} in a dependentcontext, by either using @code{this->i} (remember that @code{this} is oftype @code{Derived<T>*}, so is obviously dependent), or using@code{Base<T>::i}.  Alternatively, @code{Base<T>::i} might be broughtinto scope by a @code{using}-declaration.Another, similar example involves calling member functions of a baseclass:@smallexample  template <typename T> struct Base @{      int f();  @};  template <typename T> struct Derived : Base<T> @{      int g() @{ return f(); @};  @};@end smallexampleAgain, the call to @code{f()} is not dependent on template arguments(there are no arguments that depend on the type @code{T}, and it is alsonot otherwise specified that the call should be in a dependent context).Thus a global declaration of such a function must be available, sincethe one in the base class is not visible until instantiation time.  Thecompiler will consequently produce the following error message:@smallexample  x.cc: In member function `int Derived<T>::g()':  x.cc:6: error: there are no arguments to `f' that depend on a template     parameter, so a declaration of `f' must be available  x.cc:6: error: (if you use `-fpermissive', G++ will accept your code, but     allowing the use of an undeclared name is deprecated)@end smallexampleTo make the code valid either use @code{this->f()}, or@code{Base<T>::f()}.  Using the @option{-fpermissive} flag will also letthe compiler accept the code, by marking all function calls for which nodeclaration is visible at the time of definition of the template forlater lookup at instantiation time, as if it were a dependent call.We do not recommend using @option{-fpermissive} to work around invalidcode, and it will also only catch cases where functions in base classesare called, not where variables in base classes are used (as in theexample above).Note that some compilers (including G++ versions prior to 3.4) get theseexamples wrong and accept above code without an error.  Those compilersdo not implement two-stage name lookup correctly.@node Temporaries@subsection Temporaries May Vanish Before You Expect@cindex temporaries, lifetime of@cindex portions of temporary objects, pointers toIt is dangerous to use pointers or references to @emph{portions} of atemporary object.  The compiler may very well delete the object beforeyou expect it to, leaving a pointer to garbage.  The most common placewhere this problem crops up is in classes like string classes,especially ones that define a conversion function to type @code{char *}or @code{const char *}---which is one reason why the standard@code{string} class requires you to call the @code{c_str} memberfunction.  However, any class that returns a pointer to some internalstructure is potentially subject to this problem.For example, a program may use a function @code{strfunc} that returns@code{string} objects, and another function @code{charfunc} thatoperates on pointers to @code{char}:@smallexamplestring strfunc ();void charfunc (const char *);voidf ()@{  const char *p = strfunc().c_str();  @dots{}  charfunc (p);  @dots{}  charfunc (p);@}@end smallexample@noindentIn this situation, it may seem reasonable to save a pointer to the Cstring returned by the @code{c_str} member function and use that ratherthan call @code{c_str} repeatedly.  However, the temporary stringcreated by the call to @code{strfunc} is destroyed after @code{p} isinitialized, at which point @code{p} is left pointing to freed memory.Code like this may run successfully under some other compilers,particularly obsolete cfront-based compilers that delete temporariesalong with normal local variables.  However, the GNU C++ behavior isstandard-conforming, so if your program depends on late destruction oftemporaries it is not portable.The safe way to write such code is to give the temporary a name, whichforces it to remain until the end of the scope of the name.  Forexample:@smallexampleconst string& tmp = strfunc ();charfunc (tmp.c_str ());@end smallexample@node Copy Assignment@subsection Implicit Copy-Assignment for Virtual BasesWhen a base class is virtual, only one subobject of the base classbelongs to each full object.  Also, the constructors and destructors areinvoked only once, and called from the most-derived class.  However, suchobjects behave unspecified when being assigned.  For example:@smallexamplestruct Base@{  char *name;  Base(char *n) : name(strdup(n))@{@}  Base& operator= (const Base& other)@{   free (name);   name = strdup (other.name);  @}@};struct A:virtual Base@{  int val;  A():Base("A")@{@}@};struct B:virtual Base@{  int bval;  B():Base("B")@{@}@};struct Derived:public A, public B@{  Derived():Base("Derived")@{@}@};void func(Derived &d1, Derived &d2)@{  d1 = d2;@}@end smallexampleThe C++ standard specifies that @samp{Base::Base} is only called oncewhen constructing or copy-constructing a Derived object.  It isunspecified whether @samp{Base::operator=} is called more than once whenthe implicit copy-assignment for Derived objects is invoked (as it isinside @samp{func} in the example).G++ implements the ``intuitive'' algorithm for copy-assignment: assign alldirect bases, then assign all members.  In that algorithm, the virtualbase subobject can be encountered more than once.  In the example, copyingproceeds in the following order: @samp{val}, @samp{name} (via@code{strdup}), @samp{bval}, and @samp{name} again.If application code relies on copy-assignment, a user-definedcopy-assignment operator removes any uncertainties.  With such anoperator, the application can define whether and how the virtual basesubobject is assigned.@node Protoize Caveats@section Caveats of using @command{protoize}The conversion programs @command{protoize} and @command{unprotoize} cansometimes change a source file in a way that won't work unless yourearrange it.@itemize @bullet@item@command{protoize} can insert references to a type name or type tag beforethe definition, or in a file where they are not defined.If this happens, compiler error messages should show you where the newreferences are, so fixing the file by hand is straightforward.@itemThere are some C constructs which @command{protoize} cannot figure out.For example, it can't determine argument types for declaring apointer-to-function variable; this you must do by hand.  @command{protoize}inserts a comment containing @samp{???} each time it finds such avariable; so you can find all such variables by searching for thisstring.  ISO C does not require declaring the argument types ofpointer-to-function types.@itemUsing @command{unprotoize} can easily introduce bugs.  If the programrelied on prototypes to bring about conversion of arguments, theseconversions will not take place in the program without prototypes.One case in which you can be sure @command{unprotoize} is safe is whenyou are removing prototypes that were made with @command{protoize}; ifthe program worked before without any prototypes, it will work againwithout them.@opindex Wtraditional-conversionYou can find all the places where this problem might occur by compilingthe program with the @option{-Wtraditional-conversion} option.  Itprints a warning whenever an argument is converted.@itemBoth conversion programs can be confused if there are macro calls in andaround the text to be converted.  In other words, the standard syntaxfor a declaration or definition must not result from expanding a macro.This problem is inherent in the design of C and cannot be fixed.  Ifonly a few functions have confusing macro calls, you can easily convertthem manually.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -