在学习C++11前,请确定编辑器打开了C11标准!
int main(){
cout<<__cplusplus<<endl;//输出201103而不是别的
return 0;
}
头文件的改变
#include //新的头文件为
#include //兼容旧的写法
新引入的nullptr
在c++中如果表示空指针语义时建议使用nullptr而不要使用NULL,因为NULL本质上是个int型的0,其实不是个指针。
auto:在编译时期进行通过 = 右边的类型推导出变量类型。
// 10是int型,可以自动推导出a是int,使用 auto 类型推导的变量必须马上初始化,因为 auto 在 C++11 中只是“占位符”,并非如 int 一样的真正的类型声明。
auto a = 10;
// &a 的结果是一个 int* 类型的指针,所以推导出变量 b 的类型是 int*
auto b = &a;
// 双引号""围起的字符串是 const char* 类型,所以推导出变量 url 的类型是 const char*,即常量指针
auto url = "http://c.biancheng.net/cplus/";
// 当 = 右边的表达式是一个引用类型时,auto 会把引用抛弃,直接推导出它的原始类型。
auto &c1 = a; //c1 为 int&,auto 推导为 int
auto c2 = c1; //c2 为 int,auto 推导为 int
// auto 与 const 结合使用时,当类型不为引用时,auto 的推导结果将不保留表达式的 const 属性;
const auto d = a; // d是const int
auto e = d; // e是int
// auto 与 const 结合使用时,当类型为引用时,auto 的推导结果将保留表达式的 const 属性。
const auto& f = a; // f是const int&
auto &g = f; // g是const int&
auto的一些使用限制规则
auto d = 0, f = 1.0; // error,0和1.0类型不同,对于编译器有二义性,没法推导
auto e; // error,使用auto必须马上初始化,否则无法推导类型
auto c[10] = a; // error,auto不能定义数组,可以定义指针
vector<auto> f = d; // error,auto无法推导出模板参数
void func(auto value) {} // error,auto不能用作函数参数
class A {
auto a = 1; // error,在类中auto不能用作非静态成员变量
};
auto的使用场景
// 一、简化迭代器的定义
vector< vector<int> > v;
vector< vector<int> >::iterator i = v.begin();
auto i = v.begin(); //auto 可以根据begin() 函数的返回值类型来推导出变量 i 的类型
// 二、泛型编程,用于不知道变量类型,不希望指明具体类型的时候
#include
using namespace std;
class A{
public:
static int get(void){
return 100;
}
};
class B{
public:
static const char* get(void){
return "http://c.biancheng.net/cplus/";
}
};
template <typename T>
void func(void){
auto val = T::get();
cout << val << endl;
}
int main(void){
func<A>();
func<B>();
return 0;
}
decltype:在编译时期根据表达式类型推导出变量类型
auto 并不适用于所有的自动类型推导场景,在某些特殊情况下 auto 用起来非常不方便,甚至压根无法使用,所以 decltype 关键字也被引入到 C++11 中。
decltype推导的规则
// b是const int&,auto 要求变量必须初始化,而 decltype 不要求。
int i = 1;
decltype(i) b; //b被推导为int类型
decltype(exp) ;
// exp 这个普通的表达式可以是任意复杂的形式,但是必须要保证 exp 的结果是有类型的,不能是 void;
//例如,当 exp 调用一个返回值类型为 void 的函数时,exp 的结果也是 void 类型,此时就会导致编译错误。
decltype(10.8) x = 5.5; //x 被推导成了 double
decltype(x + 100) y; //y 被推导成了 double
//函数声明
int& func_int_r(int, char); //返回值为 int&
int&& func_int_rr(void); //返回值为 int&&
int func_int(double); //返回值为 int
const int& fun_cint_r(int, int, int); //返回值为 const int&
const int&& func_cint_rr(void); //返回值为 const int&&
//decltype类型推导,这里 exp 中调用函数时需要带上括号和参数,但这仅仅是形式,并不会真的去执行函数代码。
int n = 100;
decltype(func_int_r(100, 'A')) a = n; //a 的类型为 int&
decltype(func_int_rr()) b = 0; //b 的类型为 int&&
decltype(func_int(10.5)) c = 0; //c 的类型为 int
decltype(fun_cint_r(1,2,3)) x = n; //x 的类型为 const int &
decltype(func_cint_rr()) y = 0; // y 的类型为 const int&&
class Base{
public:
int x;
};
int main(){
const Base obj;
//带有括号的表达式
decltype(obj.x) a = 0; //obj.x 为类的成员访问表达式,符合推导规则一,a 的类型为 int
decltype((obj.x)) b = a; //obj.x 带有括号,符合推导规则三,b 的类型为 int&。
//加法表达式
int n = 0, m = 0;
decltype(n + m) c = 0; //n+m 得到一个右值,符合推导规则一,所以推导结果为 int
decltype(n = n + m) d = c; //n=n+m 得到一个左值,符号推导规则三,所以推导结果为 int&
return 0;
}
关于decltype的使用
auto 的语法格式比 decltype 简单,所以在一般的类型推导中,使用 auto 比使用 decltype 更加方便;
但是在一些auto不方便使用的场景下,decltype会更加好用,以下举例
1、auto推导必须初始化变量,而decltype只需要根据表达式即可,如果不想初始化变量即声明类型时;
2、auto 只能用于类的静态成员,不能用于类的非静态成员(普通成员),如果我们想推导非静态成员的类型时(详细的一个例子:http://c.biancheng.net/view/7151.html)
3、decltype 和 auto 结合起来完成返回值类型的推导(具体的例子http://c.biancheng.net/view/3727.html)
详细的看这个–>左值引用、右值引用、移动语义、完美转发的所有 - - 程序喵大人
1、模板的右尖括号
C++11之前是不允许两个右尖括号出现的,会被认为是右移操作符,所以需要中间加个空格进行分割,避免发生编译错误。
int main() {
std::vector<std::vector<int>> a; // error
std::vector<std::vector<int> > b; // ok
}
2、模板的别名(using代替typedef)
C++11引入了using,可以轻松的定义别名且简洁易读
typedef std::vector<std::vector<int>> vvi; // before c++11
using vvi = std::vector<std::vector<int>>; // c++11
因为:using 语法和 typedef 一样,并不会创造新的类型,
所以:func_t 定义的 xx_2 并不是一个由类模板实例化后的类,而是 void(*)(int, int) 的别名。
/* C++98/03 */
template <typename T>
struct func_t
{
typedef void (*type)(T, T);
};
// 使用 func_t 模板
func_t<int>::type xx_1;
/* C++11 */
template <typename T>
using func_t = void (*)(T, T);
// 使用 func_t 模板
func_t<int> xx_2;
除此之外,typedef定义函数指针简直太难读懂!使用using就好很多
typedef void (*func)(int, int); // 啥玩意,看不懂
using func = void (*)(int, int); // 起码比typedef容易看的懂吧
3、函数模板的默认模板参数
C++11之前只有类模板支持默认模板参数,函数模板是不支持默认模板参数的,C++11后都支持。
template <typename T, typename U=int>
class A {
T value;
};
template <typename T=int, typename U> // error
class A {
T value;
};
且类模板的默认模板参数必须从右往左定义,而函数模板则没有这个限制。对于函数模板,参数的填充顺序是从左到右的。
template <typename R, typename U=int>
R func1(U val) {
return val;
}
template <typename R=int, typename U>
R func2(U val) {
return val;
}
int main() {
cout << func1<int, double>(99.9) << endl; // 99
cout << func1<double, double>(99.9) << endl; // 99.9
cout << func1<double>(99.9) << endl; // 99.9
cout << func1<int>(99.9) << endl; // 99
cout << func2<int, double>(99.9) << endl; // 99
cout << func1<double, double>(99.9) << endl; // 99.9
cout << func2<double>(99.9) << endl; // 99.9
cout << func2<int>(99.9) << endl; // 99
return 0;
}
参考文章:
1. C++新特性汇总–腾讯云
2. C++的auto–c语言中文网
3. C++新特性汇总–知乎
C++以下选项中那种变量类型没有布尔值?
A 、int B、char C、void D、double
答:void 是一种空类型,不可存储具体的值,因此没有布尔值
在C++中,还可以使用 const 关键字来限制类的对象被复制。将类的拷贝构造函数和赋值运算符声明为 const 可以防止对象被复制,但允许对象进行移动语义的操作。此外,也可以将类的拷贝构造函数和赋值运算符声明为删除的,从而完全禁止对象的复制操作。
在C++中,异常规格说明已被弃用,取而代之的是 noexcept 关键字。
noexcept 是一个类型修饰符,用于指定函数是否会抛出异常。如果一个函数被声明为 noexcept,则它保证不会抛出任何类型的异常,包括其内部可能抛出的异常。如果函数确实抛出了异常,程序将调用 std::terminate() 终止程序执行。这有助于提高代码的可读性和可维护性,并允许编译器进行更好的优化。
持续更新中…挖坑…