if (i < j < k) // 若k大于1则为真
原因:i++需要将原始值保留下来,会造成浪费。如果是迭代器类型,这种操作消耗就很大了
i = fa(x) + fb(y); //fa和fb执行顺序不确定 *beg = toupper(*beg++); //错误:该赋值语句未定义
运算表达式中各项执行顺序不固定,上面第二个,编辑器有两种处理:
1、beg = toupper(beg); //先处理左边
2、(beg+1) = toupper(beg); //先处理右边
cout << grade < 60 ? 1 : 0; //错误: 试图比较cout和60
C++中每一个对象所占用的空间大小,是在编译的时候就确定的,在模板类没有真正的被使用之前,编译器是无法知道,模板类中使用模板类型的对象的所占用的空间的大小的。只有模板被真正使用的时候,编译器才知道,模板套用的是什么类型,应该分配多少空间。这也就是模板类为什么只是称之为模板,而不是泛型的缘故。
无花括号的if-else,且if多于else时,else匹配最近的未被匹配的if
int a = 6; int b = 6; if (a > 5) if (b > 7) cout << "log1" << endl; else cout << "log2" << endl; // 最终输出log2
因为函数结束后,局部变量就被释放掉了。
内存耗尽时,new会抛出std::bad_alloc错误
但是我们可以改变new的使用方式来阻止
int *p1 = new (nothrow) int; //如果失败,返回一个空指针
通常实现是:先深拷贝右侧的值到临时变量,然后销毁左侧的值,再调用左侧的拷贝构造。目的是为了防止两侧指向同一个地址,如果先销毁左侧的值,可能右侧会访问到野指针。
为了防止在使用临时变量的过程中产生不必要的数据拷贝,引入了“对象移动”的概念。
简单理解就是移交一些临时变量的所有权,在程序中一些变量的生存期非常短,比如 部分表达式产生的值,后增运算符i++,函数返回值等等,而我们可以在他们即将销毁之前把他们的所有权“移动”(转移)给其他变量,它们在内存中的位置是不变的,也就是没有创建新的对对象,只不过归属于其他变量了,生存期也就随着所属的变量而变化了。
#include <iostream> #include <string> std::string foo() { return std::string("abc"); } int main() { std::string s3 = foo(); //这样的写法会调用拷贝构造函数 const std::string &s = foo(); //可以使用这个值,但是不能修改他 std::string &&s2 = foo(); //右值引用 }
当用于一个指向模板参数类型的右值引用函数参数(T&&)时,forward会保持实参类型的所有细节
// 完美转发的翻转函数 template <typename F, typename T1, typename T2> void flip(F f, T1 &&t1, T2 &&t2) { f(forward<T2>(t2), forward<T1>(t1)); }