C++基本语法(下)

来源:互联网 发布:小米3d电视的数据 编辑:程序博客网 时间:2024/05/29 08:37

46.   重载针对数组分配的操作符new[]()delete[]()的重载。new[]()操作符,返回类型是void *,并且第一个参数类型是size_t。如 void *operator new[](size_t); 当调用如下代码时:Screen *ps = new Screen[10];操作符new[]()size_t参数被自动初始化,其值等于存放10Screen对象的数组所需内存的字节大小。通用可以定义定位的newdelete操作符。如void *operator new(size_t, Screen *);

47.  用户自定义转换函数可以在类类型和转换函数中指定的类型之间的转换。如:operator int();关键字operator之后的名字不一定必须是内置类型的名字,可以使用typedef定义的名字或者类名称等,但是不能为数组或者函数类型。注意,没有返回值

48.   在一个类的构造函数中,凡只带一个参数的构造函数,都定义了一组隐式转换,把构造函数的参数类型转换为该类的类型。可以使用关键字explicit来阻止编译器使用该构造函数进行隐式类型转换,但是该函数还是可以被用来执行类型转换,只要显式以强制转换。如类Number定义这样一个构造函数 Number(const SmallInt &); 有这样的函数void func(Number); 则代码在未使用explicit下可以使用SmallInt si; func(si); 在使用explicit后可以使用func(Number(si))或者func(static_cast<Number>(si));

49.  类模板参数也可以是一个非类型模板参数,绑定给非类型模板参数的表达式必须是一个常量表达式,即它能在编译时被计算。非const对象的值不是一个常量表达式,不能被用作非类型参数的实参,但是名字空间中任何对象的地址是一个常量表达式。如int i = 5;i不能用作非类型模板参数,但是&i可以。

50.  被定义在类模板定义之外的成员函数必须使用特殊的语法,来指明它是一个类模板的函数。如类Queue类的构造函数:template <typename Type> Queue <Type>::Queue() {…}。类模板的成员函数本身也是一个模板,标准C++要求这样的成员函数只有在被调用或者取地址时才被实例化,当类模板被实例化时,类模板的成员函数并不自动被实例化。

51.   有三种友元声明可以出现在类模板中:(1)非模板友元函数或者友元类;(2)绑定的友元类模板或者函数模板。如有模板函数和模板类如下:
template <typename Type> class foobar {…}
template <typename Type> void foo(QueueItem<Type>);
template <typename Type> class Queue { void bar(); … }
在类QueueItem增加以上的友元声明:
template <typename Type> class QueueItem {
friend class foobar<Type>;
friend void foo<Type>(QueueItem<Type>);
friend void Queue<Type>::bar(); …}
(3)
非绑定的友元类模板或者函数模板。如下代码:

template <typename Type>class QueueItem {
template <typename T> friend class foobar;
template <typename T> friend void foo(QueueItem<T>);
template <typename T> friend void Queue<T>::bar(); … }

52.  模板类成员也可以声明静态成员变量,类模板的每个实例都有自己的一组静态数据成员静态数据成员的模板定义必须出现在类模板定义之外,定义语法类似与类模板定义外的成员函数定义语法。只有当程序使用静态数据成员时,它才从模板定义中被真正实例化。类模板的静态成员本身就是一个模板,静态数据成员的模板定义不会引起任何内存被分配,只有对静态数据成员的某个特定的实例才会分配内存。

53.   类模板的嵌套类型,当外围类被实例化时,它的嵌套类不会自动被实例化,只有当上下文环境缺省需要嵌套类的完整类型时,嵌套类才会被实例化。

54.   函数模板或者类模板可以是一个普通类的成员,也可以是一个类模板的成员,成员模板的定义类似与一般模板的定义。而一个成员模板的定义在模板外部时,如:
template <class T> class Queue {
template <class Iter> void assign(Iter first, Iter last); … }
template <class T> template <class Iter> void Queue<T>::assign(Iter first, Iter last) {…}

55.   模板类的显式实例声明,实例代码如下:
template <typename Type> class Queue {…}
template class Queue <int>; //
显式实例了int类型的Queue
模板类的特化,类似与函数模板的特化,为指定类型的做特别的处理,如

template<> class Queue<int> {…}
模板类的特化,支持部分特化,即,如果类模板有一个以上的模板参数,指定其中一个或者几个(非全部)的参数,称为部分特化。如:
template <int hi, int wid> class Screen {…};
template <int hi> class Screen<hi, 80> {…}

56.   基类成员名在派生类中被重用时,基类的成员将被隐藏,因为基类的成员属于基类的域。对于成员函数,可以使用using声明来导入基类的成员以构成函数重载。另外一个注意事项是,派生类可以访问其基类的protected的成员,但是不能访问其他的基类成员。如代码 bool NameQuery::compare(const Query *pquery); 该函数可以访问自己基类的成员,但是无法访问pqueryprotected的成员,这种形式的访问限制不适用于自己类的其他对象,也就是bool NameQuery::compare(const NameQuery *pquery)中可以访问pqueryprotected成员。

57.   派生类由一个或多个基类子对象以及派生类部分构成。派生类的构造函数次序永远是(1)基类构造函数;(2)成员类对象构造函数;(3)派生类构造函数。这个次序跟构造函数初始化列表中的顺序无关。析构函数的次序正好与构造函数次序相反。

58.   当用类域操作符调用虚拟函数时,改变了虚拟机制,使得虚拟函数在编译时刻被静态解析。如Query是基类,NameQuery是派生类,代码:Query *pquery = new NameQuery(); pquery->isA(); pquery->Query::isA(); 则两个isA函数调用不同,前一个使用虚拟机制调用NameQuery的函数isA,而后一个直接调用基类的函数。有定义的纯虚拟函数也支持被静态调用。

59.   如果通过基类指针或引用调用派生类实例,则传递给它的缺省实参由基类指定的。

60.   如果用基类的指针来删除一个派生类的对象,为了能正确执行,必须把基类的析构函数设置为virtual的。

61.   虽然不能把new操作符声明为虚拟的,但是可以提供一个代理new操作符,来负责对象分配并拷贝到空闲存储区,通常成为clone,以下是一个例子:virtual Query *clone() {return new NameQuery(*this); }

62.   派生类的赋值操作符operator =调用基类的赋值操作符,可以有两个语法:(1) this->Query::operator =(rhs); (2)(*static_cast<Query *>(this))=rhs;

63.   多继承中,基类构造函数被调用的顺序以类派生表中被声明的顺序为准。析构函数的调用顺序总是与构造函数顺序相反。

64.   public派生被称为类型继承,派生类是基类的子类型,派生类反应了一种is-a关系private派生被成为实现继承,派生类不直接支持基类的接口,它把基类的整个共有接口在派生类中变成private,但是它希望重用基类的实现。Protected派生把基类的所有公有成员变成protected。可以通过using指示符来还原基类的访问等级,但是不能比基类中原定的级别更严格或者更不严格。组合是一种has-a的关系,在只是简单重用实现时,可以按引用(使用一个指针)来组合,组合分为按值组合(通过类的实际对象被声明为一个成员)和按引用组合(通过类对象的引用或者指针成员)

65.   虚拟继承是一种可替代“按引用组合”的继承机制(普通继承是按值组合),在虚拟继承下只有一个共享的基类。通过用关键字virtual修改一个基类的声明可以将它指定为被虚拟派生。

66.   非虚拟派生中,派生类只能显式初始化其直接基类;而在虚拟派生中,只有最终派生类可以直接调用其虚拟基类的构造函数中间派生类对基类构造函数的调用被自动抑制了,必须由最终派生类提供。构造函数顺序上,虚拟基类在所有非虚拟基类构造前被构造。在虚拟派生下,对于虚拟基类成员的继承比“该成员后来重新定义的实例(即中间派生类重载了该成员)”的权值小,所以中间派生类的成员优先,而在非虚拟派生下则产生二义性。

67.   当一个类模板被用作基类时,必须用它完整的参数表对其进行修饰,如代码:template <class Type> class derived : public Base<type> {…}

68.   RTTI(运行时刻类型识别),有两个操作符,dynamic_cast操作符:容许在运行时刻进行类型转换,它可以把一个类类型对象的指针转换成同一类层次结构中的其他类的指针,如果指针转换失败则结果为0,如果引用转换失败则抛出异常;typeid操作符,指出了指针或引用指向对象的实际派生类型。

69.   dynamic_cast操作符一次执行两个操作,它检验所请求的转换是否有效,只有有效时才进行转换,而检验过程是发生在运行时刻。它被用来执行从基类指针到派生类指针的安全转换,是一种安全的向下转换。当用于引用时,它抛出std::bad_cast异常。

70.   typeid操作符用于获取一个表达式的类型,它必须与表达式或者类型一起使用,可以是内置的类型或者类类型。可以对typeid的结果进行比较,它区分指针和对象,如class A; A a; typeid(&a)typeid(a)不同,前者是一个指针,判断指针是typeid(A *),后者是类对象。它其实返回一个类型为type_info的类对象,该类型定义在<typeinfo>。该类的构造函数都是private,无法自己创建该类的对象,只能通过typeid操作符。该类的实现依赖于编译器

71.   抛出异常:throw popOnFull(value);执行的步骤(1)throw表达式通过调用类类型pushOnFull的构造函数创建一个该类的临时对象;(2)创建一个pushOnFull类型的异常,并传递给异常处理代码,该异常对象是第一步throw表达式创建的临时对象的拷贝;(3)在开始查找异常处理代码之前,第一步中的临时对象被销毁。

72.   派生类中虚拟函数的异常规范必须与基类的对应的虚拟函数的异常规范一样或者更严格。

73.  通常情况下,在“被抛出的异常的类型”和“异常规范中指定的类型”之间不容许进行类型转换;但是,当异常规范指定一个类类型或者类类型的指针时,则可以抛出“从该类公有继承的类类型”。

74.   C++标准库中的异常层次的根类为exception,包含在头文件<exception>。它包含一个what虚拟成员函数。C++标准库将错误分为两大类,逻辑错误和运行时刻错误,并提供了一些从exception继承的类。通过new分配错误会抛出bad_alloc异常。

75.   输出操作符<<默认对C风格的字符串(即类型const char *)做了特殊处理,如果想输出字符串的地址,必须进行强制转换。ostream_iteratoristream_iterator会简化操作。

76.   其它几种常用的输入/输出操作符:(1)get(char &ch)从输入流中提前提取一个字符,包括空白字符,它返回被应用的istream对象。对应的ostream提供了put(),写入一个字符。(2)get的第二个版本和第一个版本功能一样,只是返回值不同,它返回字符值,返回类型是int因为可能返回文件尾标志,该标志通常为-1(3)get的第三个版本get(char *sink, streamsize size, char delimiter=’/n’); 读取size个字符,除非遇到字符delimiter或者遇到文件尾。该函数不把delimiter字符读入,而是将该字符继续留在流中,所以必须使用ignore来丢弃该字符。(4)getline函数类似与get的第三个版本,除了它自动丢弃了delimiter字符。想确定读入了多少字符,使用gcount函数。(5)istreamread函数和ostreamwrite函数读取指定长度的字符,除非遇到文件尾。

77.  由于不正确的格式而导致失败,istream应该把状态标志为falseis.setstate(ios_base::fallbit);对于错误状态的iostream,提取和插入操作没有影响,但是对它的测试将为false

78.   通过seekgseekp函数,可以对fstream类对象重新定位(g表示为了getting字符而定位,而p表示为了putting而定义)。类似的,tellptellg获取当前的输出和输入流的位置。

79.   每个流对象都维护了一组条件标志,通过这些条件标志,可以监视当前流的状态:(1)当一个流遇到文件结尾eof返回true(2)如果试图做一个无效操作,如seek的操作超出文件尾,则bad返回true(3)如果操作不成功,比如打开文件流对象失败或者遇到无效的输入格式,则fail返回true(4)如果其他条件都不为true,则good()true。显式修改流对象的条件状态有两种方式:(1)clear函数,可以把条件状态复位到一个显式的值;(2)使用setstate函数在现有的条件状态再增加一个条件。可用的条件值是ios_base::badbit, ios_base::eofbit, ios_base::failbit, ios_base::goodbit。同时rdstate函数可以读取当前状态值,返回类型是ios_base::iostate

原创粉丝点击