分类: C/C++
2011-12-04 18:58:13
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.
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
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内的数值SubType与ptr的乘积。
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
要把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
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
MyClass
d.assign(d); //OK
d.assign(i); //ERROR: i is MyClass
// but MyClass
}
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
void assign(const MyClass
value = x.getValue();
}
T getValue() const {
return value;
}
...
};
void f()
{
MyClass
MyClass
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
...
MyClass
MyClass
//...
}
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;
...
};
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.
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.
名空间用来解决名字冲突的问题。
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.
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.
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.
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:
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:
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:
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
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 {
the only correct and portable versions of main(). According to the C++ standard, only two definitions of main() are portable:
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.