杂七杂八

来源:互联网 发布:新古典后现代家具 知乎 编辑:程序博客网 时间:2024/05/21 17:44

input stream relevant

      从输入流对象读入数据的时候,不合法的状态为1)碰到一个 eof (end-of-file),2)或者输入的类型不符合.

cin 的 >> 操作符函数 和 getline 函数都会返回相关的 istream 对象的引用。可以把 istream 对象放在条件判断当中,当该 istream 对象不合法的时候,返回 false;否则返回 true


在C++的控制结构(control structure)中,else 会匹配它前面的,离它最近的尚未匹配的 if


case labels must be integral constant expressions


在C语言里,要在函数里改变外面的对象,一般使用指针。而在C++中,通常使用引用。


有关于C++的函数重载(overloading):

两个函数重载的基本概念是,它们的函数名相同,但是参数列表(parameter list)不同,且在相同的作用域(scope)内,返回类型是否相同不重要。至于参数列表呢,由于调用函数的时候,是caller的实参给函数的形参赋值的过程。喏,既然涉及到赋值,还记得吗,赋值的时候,赋值方和被赋值方的 top-level const 会被忽略,只考虑 low-level const。因此如果两个函数的参数列表某个对应位置,它们的形参 high-level const 的不同,并不会让编译器觉得它们是不同的。而如果存在 low-level const 不相同的情况的话,那么编译器就会视为此二函数是重载的。废话少说,上代码:

//fine -- different when considering low-level const onlyvoid print(const int &r); //reference decorated by a low-level constvoid print(int &r);//fine -- different when considering low-level const onlyvoid print(const int *p) { //pointer decorated by a low-level constcout << "this is print(const int *p).\n";}void print(int *p) {cout << "this is print(int *p).\n";}/* conflict-- redefinition of "print(int *p)" * because the qualified condition of int *p and int *const p is the same * when considering low-level const only.*/void print(int *const p) {//pointer decorated by a high-level constcout << "this is print(int *const p).\n";}



const_cast 之于 函数重载(overloading)

       之前学过const_cast,只知道它对于函数重载有特殊的意义,但是那时仍不太明白。今天看书 [1] 终于接触到了,书上只是讲了作用。并没有讲到使用 const_cast 相对于其他重载实现方法的优势所在。下面我就以书上的例子作为基础,进行扩展补足:

       有一个函数,它的参数列表中是两个 const string 类型的引用,返回值类型亦是如此。函数的功能就是去返回字符串字典序较小的那个字符串的引用。代码如下所示:

const string &shorterString(const string &s1 , const string &s2) {return s1.size() <= s2.size() ? s1 : s2;}

现在问题来了,如果我想重载出另外一个 shorterString 函数,且该版本要返回一个 string 类型的引用呢 ?


简单粗暴的方法,就是模仿上面的代码:

string &shorterString(string &s1 , string &s2) {return s1.size() <= s2.size() ? s1 : s2;}

       然而,这样写非常危险,因为在这个函数里面,s1 和 s2 都是裸露的非 const 修饰的引用,编程时稍有不慎,在返回之前,就可能把传进来的两个字符串都改变了,从而造成错误。


我们现在分析一下应该怎么去写呢?

       首先,由于要返回 string &,根据函数重载的概念,改变 return type 是没有用的,参数列表不变的话,仍然会 redefinition。因此要改函数列表,现在函数头就变成了 string &shorterString (string &s1 , string &s2); 这和上面那个暴力版本的是一样的。现在使用 const_cast,我们可以写成下面这样:

string &shorterString(string &s1 , string &s2) {//const_cast s1 and s2 then call function shorterString(const string & ,const string &)const string &r = shorterString(const_cast<const string &>(s1),const_cast<const string &>(s2));return const_cast<string &>(r);}


       函数体中,先将s1 和 s2 分别 cast 成用 const 修饰,代入安全的 const string &shorterString(const string &s1, const string &s2) 函数。得到 常量引用 r,由于我们要返回 string &,就把 r 的 const 给 cast 掉,然后再返回。既满足了功能的要求,又全程安全的访问两个引用。

综上,使用 const_cast 能你的函数重载更加安全哦 !

       如果函数的一个参数有默认参数,那么该参数后面的所有参数都必须要有默认参数。在设计函数的时候,有一个好的编程习惯 --- 把不太可能使用默认参数(变动较大)的参数放在参数列表前面,把经常使用默认参数的参数放在参数列表的后面。


C++中的literal type(直接量类型)只存在于:内置算术类型(arithmetic)、指针类型、引用类型、literal class、枚举。

而不存在于其他的复合类型,如 string、IO class 等,都不是 literal type。([1] 2.4.4, p. 66)


一个常量表达式函数(constexpr修饰)不一定要返回一个常量,只要它的返回值类型和形参列表类型都是 literal type 就行了。


函数匹配:

1. 对于调用函数的语句,找到它能看见的同名函数,此为 candidate functions.

2. 对于candidate functions,按该调用语句的传参匹配 candidate functions,实参和形参个数对应一致(有默认参数的函数更加灵活),且对应类型要一致或者可以转化(实参类型 到 形参类型)。找到满足这样条件的函数,此为 viable functions.

3. 对于 viable functions,按下列两个条件选择最优匹配(best match)

            1. 自己的形参对实参的匹配情况,总体不差于任何一个其他的 viable function

            2. 至少存在一个形参和实参的对应,匹配得比任何一个其他的 viable function 都要好


(匹配情况比较级的标准:按rank分情况,参看[1] p. 245, 6.6.1 Argument Type Conversions)


最后,如果最终得到的 best match 大于一个,编译器报错 --- ambiguous。否则就匹配成功


在调用函数传参的时候,参数类型转换中的完全匹配(exact match)含有 top-level const 的情况。意即如果你想重载两个函数,但是这两个函数形参列表中唯一的不同只是 --- 某些对应位置的形参修饰的 top-level const 情况不同的话,编译器会视为重定义(redefinition)。举例如下:

int calc(char *, char *) {}//redefinition of calc (only top-level const differs)int calc(char *const, char *const) {}int comp(int) {}//redefinition of comp (only top-level const differs)int comp(const int) {}


类中定义(define)的方法,都会被认为是 inline 的方法


       使用 top-level const 修饰的类对象(以下简称 const 对象),不可以调用本类的非 const 方法。因为,类对象在调用类方法的时候,会把自己的地址传到类方法的参数列表的隐藏参数 this 指针中。其中此指针默认的类型是 type *const(使用 high-level const修饰,为了让this指针始终只指向一个对象)。如果 用 top-level const 修饰的类对象调用 非const 类方法的时候,就会发生 “让一个指向普通对象的const 指针指向一个const 对象”的情况,这是不合法的。那么,如果我想让const 对象调用非const方法怎么办呢,很简单 --- 在 非const方法后面加上 const,这样一来,方法的参数列表中的隐藏参数 this 指针的类型就变成了 const type *const 了,这样就合法了。那么当然,你亦不可以在这个方法中修改类的字段(field)了。


当定义个 const 修饰的 class type 对象的时候,由于这个 "const" 是在构造函数执行后才生效,所以我们可以在构造函数中初始化这个对象的成员,给它赋值。


显示自定义无参数的构造函数的原因:

1. 定义了带参数的构造函数之后,编译器不会帮我们生成默认构造函数了,我们要自己定义。--- 创建一个,负责所有

2. 编译器生成的默认构造函数,可能会对类的成员进行undefined的初始化。比如对一些 built-in type:int、double... 还有部分 compound type:pointer、array ... ---防止类成员未定义的初始化

3. 如果有一个成员是另一个类的对象,且该类无默认构造函数,那编译器生成的默认构造函数都不知道怎么去初始化这个成员了。


类成员(member/field)的初始化顺序:

1. 构造函数的成员初始化列表(member initializer list 或曰 constructor initializer list)

2. 若某些成员没有在1中显示初始化,则使这些成员用in-class initializer list 进行初始化(C++ 11 extension),或者使用默认初始化(by default)

3. 执行构造函数中的函数体进行初始化



A const member function that returns *this as a reference should have a return type that is a reference to const


因为 this 的 native 声明为 "type * const this"(地址固定),若是 const member function,那么 this 的声明就变成了 

"const type * const this",此时 *this 得到一个 const object,如果 member function 要返回一个引用的话,那么只能返回一个 指向当前对象的 const 引用。


在类中声明(declaration)友元函数,无论你是否定义(define)了它,你都需要在类外面再声明一次,因为在类中的那个不算是真正的声明,只是告诉类 --- 你有个友元函数而已。


C++ 不允许连续进行两次隐式转换(implicit conversion)


使用 auto 进行自动类型推导,只能推导出 low-level const,high-level const 需要自己手动添加。


C++ STL中有类型 array,这个类型和数组类型 [ ] 很像,不同的是,array 这个类型的操作比 [ ] 丰富,比如可以为 array 的对象赋另外一个对象,然而我们可不能把一个数组赋给另外一个数组。

另外,对于STL中的容器(除array外),swap 函数只是交换内部的数据结构(internal structure),而不是实际上去交换元素,因此速度很快,为常数时间。然而,对array类型使用 swap 函数就真的是傻瓜式地去交换两个数组的元素了。


References

[1] 《C++ Primer Fifth Edition》  Stanley B. Lippman, Josée Lajoie, Barbara E. Moo



1 0
原创粉丝点击