Chinaunix首页 | 论坛 | 博客
  • 博客访问: 631083
  • 博文数量: 87
  • 博客积分: 3399
  • 博客等级: 中校
  • 技术积分: 1422
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-17 21:20
文章分类

全部博文(87)

文章存档

2013年(1)

2012年(51)

2011年(33)

2010年(2)

分类: C/C++

2011-12-04 18:58:13

Chapter2 C++及其标准程序库简介 introduction to C++ and the Standard Library
 2.1历史 History

The standardization of C++ was started in 1989 and finished at the end of 1997, although some formal motions delayed the final publication until September 1998.

The standard has the title "Information Technology — Programming Languages — C++." Its document number is ISO/IEC 14882-1998

in 1994, when people thought the standard was close to being finished, the STL was incorporated, which changed the whole library radically. However, to get finished, the thinking about major extensions was eventually stopped, regardless of how useful the extension would be. Thus, hash tables are not part of the standard, although they should be a part of the STL as a common data structure.

2.2 新语言特性 2.2.1模板Templates

A template is not compiled once to generate code usable for any type; instead, it is compiled for each type or combination of types for which it is used. This leads to an important problem in the handling of templates in practice: You must have the implementation of a template function available when you call it, so that you can compile the function for your specific type. Therefore, the only portable way of using templates at the moment is to implement them in header files by using inline functions.

Template 并非一次编译就可以生成适合所有类型的代码,而是针对所使用的某个或某组类型进行编译。导致一个重要问题:调用template function时,必须提供一个template function的实现。目前为了使运用template的程序可移植,唯一的方法是在头文件中以inline function 实现templatefunction

新的标准模板特性:

1)无类型参数Nontype Template Parameters

In addition to type parameters, it is also possible to use nontype parameters. A nontype parameter is then considered as part of the type.

bitset<32> fIags32;     // bitset with 32 bits

    bitset<50> flags50;     // bitset with 50 bits

These bitsets have different types because they use different template arguments. Thus, you can't assign or compare them (except if a corresponding type conversion is provided).

上面两个类型是不同的,因为它们使用了不同的模板参数,所以不能相互赋值或比较。

2)默认模板参数Default Template Parameters

   template >

   class MyClass;

If you pass only one argument, the default parameter is used as second argument:

   MyClass x1;          // equivalent to: MyClass >

Note that default template arguments may be defined in terms of previous arguments.

默认模板参数可以依照前面的参数而定义。

3)关键字typename

The keyword typename was introduced to specify that the identifier that follows is a type. Consider the following example:

   template

   Class MyClass {

      typename T::SubType * ptr;

      ...

   };

Here, typename is used to clarify that SubType is a type of class T. Thus, ptr is a pointer to the type T::SubType. Without typename, SubType would be considered a static member. Thus

   T::SubType * ptr

would be a multiplication of value SubType of type T with ptr.

Typename用来指出后面的标示符是一个类型。

如果没有typename,上面的语句会被解释为类型T内的数值SubTypeptr的乘积。

the subtype could also be an abstract data type (such as a class):

   class Q {

       class SubType;

       ...

   };

Note that typename is always necessary to qualify an identifier of a template as being a type, even if an interpretation that is not a type would make no sense. Thus, the general rule in C++ is that any identifier of a template is considered to be a value, except it is qualified by typename.

Apart from this, typename can also be used instead of class in a template declaration:

   template class MyClass;

要把template中的一个标示符指定为某种类型,即使意图显而易见,typename也是比不可少的 c++的一般规则是:除非被typename修饰,否则模板中的任何标示符都会被认为是一个值而非类别。

4)成员模板Member Templates

Member functions of classes may be templates. However, member templates may not be virtual, nor may they have default parameters. For example:

   class MyClass {

       ...

       template

       void f(T);

   };

This feature is often used to support automatic type conversions for members in template classes. For example, in the following definition the argument x of assign() must have exactly the same type as the object it is called for:

                                                       

   template

   class MyClass {

     private:

       T value;

     public:

       void assign(const MyClass& x) { // x must have same type as *this

           value = x.value;

       }

       ...

   };

It would be an error to use different template types for the objects of the assign() operation even if an automatic type conversion from one type to the other is provided:

                                                       

   void f()

   {

       MyClass d;

       MyClass i;

 

       d.assign(d);   //OK

       d.assign(i);   //ERROR: i is MyClass

                      //        but MyClass is required

   }

By providing a different template type for the member function, you relax the rule of exact match. The member template function argument may have any template type, then as long as the types are assignable:

   template

   class MyClass{

     private:

       T value;

     public

       template                       // member template

       void assign(const MyClass& x) {      // allows different template types

           value = x.getValue();

       }

       T getValue() const {

           return value;

       }

       ...

   };

   void f()

   {

       MyClass d;

       MyClass i;

 

       d.assign(d);   // OK

       d.assign(i);   // OK (int is assignable to double)

   }

Note that the argument x of assign() now differs from the type of *this. Thus, you can't access private and protected members of MyClass<> directly. Instead, you have to use something like getValue() in this example.

A special form of a member template is a template constructor. Template constructors are usually provided to enable implicit type conversions when objects are copied. Note that a template constructor does not hide the implicit copy constructor. If the type matches exactly, the implicit copy constructor is generated and called. For example:

模板构造函数在拷贝对象时用来提供隐式类型转换。

   template

   class MyClass {

     public:

       //copy constructor with implicit type conversion

       //- does not hide implicit copy constructor

       template

       MyClass(const MyClass& x);

       //...

   };

   void f()

   {

       MyClass xd;

       ...

       MyClass xd2(xd);     // calls built-in copy constructor

       MyClass xi (xd);        // calls template constructor

       //...

   }

Here, the type of xd2 is the same as the type of xd, so it is initialized via the built-in copy constructor. The type of xi differs from the type of xd, so it is initialized by using the template constructor. Thus, if you write a template constructor, don't forget to provide a copy constructor, if the default copy constructor does not fit your needs.

(5)Nested Template Classes

Nested classes may also be templates:

                                                       

   template

   class MyClass {

       ...

       template

       class NestedClass;

       ...

   };

2.2.2基本类型的显式初始化 Explicit Initialization for Fundamental Types

If you use the syntax of an explicit constructor call without arguments, fundamental types are initialized with zero:

                                              

   int i1;             // undefined value

   int i2 = int();     // initialized with zero

This feature is provided to enable you to write template code that ensures that values of any type have a certain default value. For example, in the following function the initialization guarantees that x is initialized with zero for fundamental types:

   template

   void f()

   {

       T x = T();

       ...

   }

显式调用基本类型的无参构造函数,变量会默认被初始化为0.

2.2.3异常处理 Exception Handling

The throw statement starts a process called stack unwinding; that is, any block or function is left as if there was a return statement. However, the program does not jump anywhere. For all local objects that are declared in the blocks that the program leaves due to the exception their destructors are called. Stack unwinding continues until main() is left, which ends the program, or until a catch clause "catches" and handles the exception:

                                              

   int main()

   {

       try {

           ...

           f();

           ...

       }

       catch (const Error&) {

           ... //handle exception

       }

       ...

   }

Here, any exception of type Error in the try block is handled in the catch clause.

Exceptions end a call of the function, where you find the exception, with the ability to pass an object as argument back to the caller. However, this is not a function call back in the opposite direction (from the bottom where the problem was found to the top where the problem is solved or handled). You can't process the exception and continue from where you found the exception. In this regard, exception handling is completely different from signal handling.

Exception objects are ordinary objects that are described in ordinary classes or ordinary fundamental types. Thus, you can use ints, strings, or template classes that are part of a class hierarchy. Usually you design (a hierarchy of) special error classes. You can use their state to pass any information you want from the point of error detection to the point of error handling.

Note that the concept is called exception handling not error handling. The two are not necessarily the same. For example, in many circumstances bad user input is not an exception; it typically happens. So it is often a good idea to handle wrong user input locally using the usual error-handling techniques.

You can specify which set of exceptions a function might throw by writing an exception specification:

                                              

   void f() throw(bad_alloc);  //f() may only throw bad_alloc exceptions

You can specify that a function not throw an exception by declaring an empty set of exceptions:

                                              

   void f() throw();           //f() does not throw

A violation of an exception specification causes special behavior to occur. The C++ standard library provides some general features for exception handling, such as the standard exception classes and class auto_ptr.

2.2.4 名空间 Namespaces

名空间用来解决名字冲突的问题。

Unlike classes, namespaces are open for definitions and extensions in different modules. Thus you can use namespaces to define modules, libraries, or components even by using multiple files. A namespace defines logical modules instead of physical modules (in UML and other modeling notations, a module is also called a package).

You don't have to qualify the namespace for functions if one or more argument types are defined in the namespace of the function. This rule is called Koenig lookup. For example:

   //defining identifiers in namespace josuttis

   namespace josuttis {

       class File;

       void myGlobalFunc(const File&);

       ...

   }

   josuttis::File obj;

   myGlobalFunc(obj);   //OK, lookup finds josuttis::myGlobalFunc()

A using directive makes all names of a namespace available, because they would have been declared outside their namespace. using directive 等于将这些名字声明于名空间之外。However, the usual name conflicts may arise. For example, the directive

   using namespace josuttis;

makes File and myGlobalFunc() global in the current scope. The compiler will report an ambiguity if there also exists an identifier File or myGlobalFunc() in the global scope and the user uses the name without qualification.

Note that you should never use a using directive when the context is not clear (such as in header files, modules, or libraries). The directive might change the scope of identifiers of a namespace, so you might get different behavior than the one expected because you included or used your code in another module. In fact, using directives in header files is really bad design.

The C++ standard library defines all identifiers in namespace std.

2.2.5 bool类型Type bool

To provide better support for Boolean values, type bool was introduced. Using bool increases readability and allows you to overload behavior for Boolean values. The literals true and false were introduced as Boolean values. Automatic type conversions to and from integral values are provided. The value 0 is equivalent to false. Any other value is equivalent to true.

2.2.6 explicit 关键字 Keyword explicit

explicit禁止隐式类型转换(通过赋值语句进行的),只允许显式类型转换。

By using the keyword explicit, you can prohibit a single argument constructor from defining an automatic type conversion.禁止单参构造函数进行自动类型转换。 A typical example of the need for this feature is in a collection class in which you can pass the initial size as constructor argument. For example, you could declare a constructor that has an argument for the initial size of a stack:

   class Stack {

       explicit Stack(int size);      // create stack with initial size

       ...

   };

Here, the use of explicit is rather important. Without explicit this constructor would define an automatic type conversion from int to Stack. If this happens, you could assign an int to a Stack:

   Stack s;

   ...

   s = 40;      // Oops, creates a new Stack for 40 elements and assigns it to s

The automatic type conversion would convert the 40 to a stack with 40 elements and then assign it to s. This is probably not what was intended. By declaring the int constructor as explicit, such an assignment results in an error at compile time.

Note that explicit also rules out(排除、取消) the initialization with type conversion by using the assignment syntax:

                                              

   Stack s1(40);        // OK

   Stack s2 = 40;       // ERROR

This is because there is a minor difference between

                                              

   X x;

   Y y(x);       // explicit conversion

and

                                              

   X x;

   Y y = x;      // implicit conversion

The former creates a new object of type Y by using an explicit conversion from type X, whereas the latter creates a new object of type Y by using an implicit conversion.

2.2.7 New Operators for Type Conversion

1.       static_cast

This operator converts a value logically. It can be considered a creation of a temporary object that is initialized by the value that gets converted. The conversion is allowed only if a type conversion is defined (either as a built-in conversion rule or via a defined conversion operation). For example:

   float x;   ...   cout << static_cast(x);        // print x as int   ...   f(static_cast("hello"));    // call f() for string instead of char*

2.       dynamic_cast

This operator enables you to downcast a polymorphic type to its real static type.将多态类型向下转变为实际的静态类型。 This is the only cast that is checked at runtime. 执行期检查的唯一一种类型转换。Thus, you could also use it to check the type of a polymorphic value. For example:

                                                                                                                                 class Car;         // abstract base class (has at least one virtual function)   class Cabriolet : public Car {       ...   };   class Limousine : public Car {       ...   };   void f(Car* cp)   {       Cabriolet* p = dynamic_cast(cp);       if (p == NULL) {           //p did not refer to an object of type Cabriolet           ...       }   }

In this example, f() contains a special behavior for objects that have the real static type Cabriolet. When the argument is a reference and the type conversion fails, dynamic_cast throws a bad_cast exception (bad_cast is described on page 26). Note that from a design point of view, it it always better to avoid such type-dependent statements when you program with polymorphic types.

3.       const_cast

This operator adds or removes the constness of a type. In addition, you can remove a volatile qualification. Any other change of the type is not allowed.

4.       reinterpret_cast

The behavior of this operator is implementation defined. (由实际编译器定义)It may be but is not required to reinterpret bits. Using this cast is usually not portable.

These operators replace the old cast techniques that use parentheses. They have the advantage of clarifying the intention of the conversion. The old casts with parentheses could be used for any of these type conversions except for dynamic_cast, so when they were used you could not formulate the exact reason for the conversion. The new operators enable the compiler to receive more information regarding the reason for the conversion and to report an error if the conversion does more than it should.

Note that these operators are provided for only one argument. Consider the following example:

   static_cast(15,100)     // Oops, creates Fraction(l00)

This example does not do what you might expect. Instead of initializing a temporary fraction with numerator 15 and denominator 100, it initializes a temporary fraction only with the single value 100. The comma(逗号) is not an argument separator here. Instead, it is the comma operator that combines two expressions into one expression and yields the second. 逗号表达式的值为逗号后面的表达式的值)The correct way to "convert" values 15 and 100 into a fraction is still

   Fraction(15,100)                  // fine, creates Fraction(15,100)
2.2.8 Initialization of Constant Static Members

It is now possible to initialize integral constant static members inside the class structure. This is useful when the constant is used in the class structure after the initialization. For example:   class MyClass {

       static const int num = 100;       int elems[num];       //...   };Note that you still have to define space for a constant static member that is initialized within a class definition:   const int MyClass::num;     // no initialization here
2.2.9 mian函数定义 Definition of main()

the only correct and portable versions of main(). According to the C++ standard, only two definitions of main() are portable:

   int main(){}and   int main (int argc, char* argv[]){}where argv (the array of command-line arguments) might also be defined as char**. You may, but are not required to, end main() with a return statement. Unlike C, C++ defines an implicit   return 0;at the end of main(). This means that every program that leaves main() without a return statement is successful (any value other than 0 represents a kind of failure).
 2.3 复杂度和大O表示法 Complexity and the Big-O Notation

if the runtime grows linearly with the number of elements (doubling the input doubles the runtime) the complexity is O(n). If the runtime is independent of the input, the complexity is O(1).

It is important to observe that the Big-O notation hides factors with smaller exponents (such as constant factors). In particular, it doesn't matter how long an algorithm takes. Any two linear algorithms are considered equally acceptable by this measure. There even may be some situations in which the constant is so huge in a linear algorithm that even an exponential algorithm with a small constant would be preferable in practice. This is a valid criticism of the Big-O notation. Just be aware that it is only a rule of thumb; the algorithm with optimal complexity is not necessarily the best one.具有最佳复杂度的算法并不一定是最好(最快)的算法。

lists all the categories of complexity with a certain number of elements to give you a feel of how fast the runtime grows with respect to the number of elements. As you can see, with a small number of elements the runtimes don't differ much. Here, constant factors that are hidden by the Big-O notation may have a big influence. However, the more elements you have, the bigger the differences in the runtimes, so constant factors become meaningless. Remember to "think big" when you consider complexity.当考虑复杂度时,输入量要足够大。

Table 2.2. Runtime with Respect to the Complexity and the Number of Elements

Complexity

No.of Elements

Type

Notation

1

2

5

10

50

100

1000

Constant

O(1)

1

1

1

1

1

1

1

Logarithmic

O(log(n))

1

2

3

4

6

7

10

Linear

O(n)

1

2

5

10

50

100

1,000

n-log-n

O(n * log(n))

1

4

15

40

300

700

10,000

Quadratic

O(n2)

1

4

25

100

2,500

10,000

1,000,000

Some complexity definitions in the C++ reference manual are specified as amortized. (分期摊还)This means that the operations in the long term behave as described. However, a single operation may take longer than specified.

阅读(1300) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~