C++11笔记
__func__宏
__func__返回当前的函数名,也可以返回class和struct名。
/*返回函数名hello*/
const char* hello()
{
return __func__;
}
/*返回结构体名foo*/
struct foo
{
foo():name(__func){}
const char* name;
}
__VA_ARGS__宏
可变参数宏
#define INFO(...) printf(__VA_ARGS)
noexcept
noexcept操作会阻止异常扩散,被noexcept修饰的函数,如果throw()抛出异常,则直接调用std::terminate()结束程序,catch并不能捕获到该异常。noexcept(false)表示始终抛出异常,noexcept相当于noexcept(true)操作。
花括号{}初始化
花括号初始化,统一了C++初始化方式,可以初始化类非静态成员变量、结构体、普通变量、对象等,类非静态变量初始化时,初始化列表后在就地初始化之后起作用。
struct foo
{
int a;
int b;
}f{1, 2};
class test
{
public:
test(int i):a(i){}
private:
int a{2};
int b = 4;
foo f{3,4};
};
int a{1};
test t{6};
sizeof运算符
sizeof运算符可以对类成员直接进行求值
class foo
{
public:
int a;
int b;
};
std::cout << sizeof(foo::a) << std::endl; //C++11以前不合法
final和override
final阻止派生类对基类函数的重载,override修饰的函数则要求该函数必须正确从基类虚函数继承。
class objcct
{
public:
virtual void print() = 0;
};
class base : public object
{
public:
void print() final;
};
class Derive : public base
{
public:
//编译错误
void print();
};
struct Base
{
virtual void Turing() = 0;
virtual void Dijkstra() = 0;
virtual void VNeumann(int g) = 0;
virtual void DKnuth() const;
void Print();
};
struct DerivedMid : public Base
{
void VNeumann(double g);
};
struct DerivedTop : public DerivedMid
{
void Turing() override;
void Dikjstra() override; //无法通过编译,拼写错误,并非重载
void VNeumann(double g) override; //无法通过编译,参数不一致
void DKnuth() override; //无法通过编译,非常量重载
void Print() override; //无法通过编译,非虚函数重载
};
继承构造函数
多重继承中,如果基类构造函数参数很多,那么我们在派生类中构造函数初始化列表中初始化基类,需要书写大量的代码,继承构造函数,只需要进行声明即可将基类构造函数继承过来。派生类一旦继承了基类的构造函数,则派生类不会再自动生成默认构造函数。如果派生类从多个基类中继承构造函数发生冲突,则需要派生类显示的定义此构造函数。
struct A
{
A(int i){}
A(double d, int i) {}
A(float f, int i, const char* c) {}
};
struct B : A {
using A::A;
int d{0};
};
int main(){
B b(356); //b.i = 356
B b1(2.3, 234); //b1.d = 2.3, b.i=234;
}
委托构造函数
委托构造函数主要解决,类有多个构造函数,构造函数参数个数不同时,需要书写很多的初始化代码,如下所示。委托构造函数要早于目标构造函数执行,委托构造函数和初始化列表不能同时出现,否则会出现冲突。同样一个目标构造函数既可以使用委托构造函数初始化,也可以作为其他构造函数的委托构造函数,从而形成一个委托链,但不能出现委托构造环。另外我们可以用try,catch捕获委托构造函数抛出的异常。
/*C++98写法*/
class Info{
public:
Info() : type(1), name('a') { InitRest(); }
Info(int i) : type(i), name('a') { InitRest(); }
Info(char e) : type(1), name(e) { InitRest(); }
};
/*C++11写法*/
class Info{
public:
Info() : type(1), name('a') { InitRest(); }
Info(int i) : Info() { type = i; }
Info(char e) : Info() { name = e; }
};
右值引用
左值一般指有名字的,可以取地址的值,反之无法取地址,没有名字的就是右值。例如:a = b + c; a就是左值,(b + c) 就是右值, 表达式(1 + 2)属于纯右值。左值引用T & a, 右值引用 T && a。引入右值主要为了引入移动构造函数,C++98中对类中具有指针类型变量时,一般需要重写拷贝构造函数和重载赋值操作,重新申请一块内存空间,将原来类指针内容拷贝进去,不理会原来的类是否就要释放。C++11引入移动构造函数,则可以从原来的类中将内存空间直接嫁接过来,减少内存的申请释放操作。
class foo
{
public:
foo(const int a) : m_data(new int(a)){}
//拷贝构造函数
foo(const foo& s) : m_data(new int(0))
{
if (s.m_data != nullptr)
*m_data = *(s.m_data);
}
//移动构造函数
foo(const foo && s) : m_data(s.m_data)
{
m_data = nullptr;
}
private:
int* m_data;
};
上面代码中foo类是典型的类内包含指针类型的成员函数,C++98中需要实现拷贝构造函数,分配内存,然后copy内存数据。C++11可以实现移动构造函数,移动构造函数的参数是类的右值类型,因此构造类对象时,传入的参数是右值,则使用移动构造函数构造对象。上面代码中如果没有实现移动构造函数,传入右值进行构造时,则调用拷贝构造函数。实现类时需要自己考虑是否需要实现移动构造函数,构造新对象时需要考虑是否需要通过移动构造函数构造。移动构造函数最好使用noexcept修饰,保证不会抛出异常,抛出异常非常危险,很可能导致内存泄露。
C++中提供了将左值转换为右值引用的函数std::move(左值),函数返回的是右值引用,如下代码。模板编程时由于使用typedef重定义类型,会出现多个引用符号,如下代码,C++11中规定T和TR中只要出现左值引用,则定义v按照左值引用处理,下面代码中v和v1均为左值引用。模板编程时需要对重载函数调用时,需要考虑不同参数类型如何进行传参问题,C++11提供了std::forward函数进行完美转发,无需考虑引用叠加问题,直接进行参数传递。
foo f(3);
foo t(std::move(f)); //std::move()函数将左值f转换为右值,触发foo调用移动构造函数构造t对象。
//引用叠加
typedef T& TR;
TR& v;
TR&& v1;
void DoSomething(int& a){std::cout << "int&" << std::endl; }
void DoSomething(int&& a){std::cout << "int&&" << std::endl; }
void DoSomething(const int & a){std::cout << "const int&" << std::endl; }
void DoSomething(const int && a){std::cout << "const int&&" << std::endl;}
template<typename T>
void PerfectForward(T && t)
{
DoSomething(std::forward<T>(t));
}
显式转换操作符
C++98中可以显示转换内置类型,比如bool,char*等,但是不允许显示转换自定义类型,C++11中添加了对自定义显式转换操作符支持。explicit关键字C++98中只能修饰构造函数,令对象只能进行显式的构造,C++11对其进行了扩展,可以修饰自定义转换操作符函数,令对象只能显式进行转换。
class foo
{
public:
operator bool () const
{
return true;
}
//xxx为自定义类型,可以是我们自定义类,自定结构体
operator xxx ()
{
}
//explicit关键字修饰,则foo转换为yyy只能通过显式的转换,即使用static_cast<yyy>()
explicit operator yyy ()
{
}
};
初始化列表
C++11对初始化方式进行了扩展,使用大括号可以初始化list, vector, map等数据类型,并且支持自定义类型的初始化。
std::vector<int> v = {1, 2, 3};
std::map<int, int> m = {{1, 2}, {3, 4}, {5, 6}};
模板别名
C++对using进行了扩展可以使用using给模板定义别名,达到比typedef更加强大的功能。
template<typename K, typename V>
using IntStrMap = std::map<K, V>;
IntStrMap<int, std::string> m;
SFINEA规则
SFINEA即匹配失败不是错误,下面例子C++98中标准会报编译错误,C++11对进行了扩展,允许编译通过。
struct Test {
typedef int foo;
};
template <typename T>
void f(typename T::foo) {}
template <typename T>
void f(T) {}
int main(){
f<Test>(10);
f<int>(10);
}
auto关键字
auto在C++11中被赋予了新的含义,自动推导变量类型,慎用,如果大量auto出现,势必会降低程序的可阅读性,auto并非万能,下面列出不能推导的情形。
1.不能作为函数形参类型推导。 void fun(auto i = 2);
2.结构体中非静态成员不能推导。 struct { auto i = 1; }
3.不能推导数组类型。 auto a[3] = {0};
4.不能作为模板实例化的参数。std::vector v = {1};
decltype关键字
decltype也是C++11中进行类型推导的关键字,auto类似于动态语言中的var,decltype则是从变量或返回值中推导类型。
int i = 0;
decltype(i) j = 1;
float foo() {}
decltype(foo()) a = 1.0;
decltype可以作为函数返回值推导,模板中大有用处。
auto foo(int i) -> decltype(i) { return i; }
auto会继承原来变量的const和volatile属性,decltype推导时不会继承原来的变量的const和volatile
for循环
std::vector<int> vec = {0, 1, 2};
for (auto i : vec)
std::cout << i << std::endl;
强枚举类型
C++98中枚举类型作用域是全局的,并且底层长度不定,可以隐式的进行类型转换。C++11中定义了一种强类型枚举。
1.强作用域,强类型枚举成员的名称不会被输出到其父作用域空间。
2.转换限制,强类型枚举成员的值不可以与整型隐式地相互转换。
3.可以指定底层类型,强类型枚举默认的底层类型为int,但是可以显式地指定底层类型,底层类型不可以是wchar_t类型。
enum class Type:int { General, Light, Medium, Heavy};
Type t = Type::Light;
智能指针
C++11废弃了auto_ptr指针,重新实现了三种智能指针,其实boost中很早就实现了,用法和原理差不多。
1.unique_ptr 指针只能有一个对象拥有,不可以共享,不可以复制,move操作后,原来对象指针将为空,先的对象将唯一拥有该指针。
unique_ptr<int> up1(new int(11));
unique_ptr<int> up2 = up1; //错误无法复制
unique_ptr<int> up3 = move(up1); //move操作后up3将为空,*up1将导致运行错误
2.shared_ptr 指针可以被多个对象公用,通过引用计数方式标记,计数为0时,删除指针指向内存。
3.weak_ptr 可以指向shared_ptr对象,但是weak_ptr操作不会影响shared_ptr的计数,通常用于判断shared_ptr是否有效。lock函数可以返回一个shared_ptr对象,但是不会增加计数
shared_ptr<int> sp1(new int(22));
weak_ptr<int> wp = sp;
shared_ptr<int> sp2 = wp.lock(); //并不会增加引用计数值
sp.reset(); //释放智能指针指向内存。
constexpr
constexpr 修饰的变量会被编译为常量,前提是它是常量,可以定义常量表达式函数,常量表达式编译器可以进行运算,感觉老式const只是定义常量不能进行修改,但是并不能告诉编译器编译器参与计算。constexpr却可以作为数组初始化的长度,枚举类型初始化,switch-case的case表达式通过编译,还可以修饰构造函数,从而定义一个constexpr修饰的类对象。
constexpr修饰常量表达式函数必须满足下面条件
1.函数体只有单一的return 返回语句。
2.函数必须有返回值(不能时void函数)。
3.函数使用前必须已有定义,不能只进行声明。
4.return返回语句表达式中不能使用非常量表达式的函数,全局数据,且必须是一个常量表达式,也就是调用的整个链都必须用constexpr修饰。
变长模板参数
C++98如果模板中需要传递多个参数都是通过宏或者直接手写一些特化版本,boost中bind和function实现均是如此,C++11中则提供了变长模板参数。
template<typename ...T> class TempArgs {};
template<typename Head, typename ...Tail>
class TempArgs<Head, Tail...>: private TempArgs<Tail...>
{
Head head;
};
template<>
class TempArgs<>{};
//变参模板函数
void print() { std::cout << "end" << std::endl; }
template<typename T1, typename ...T>
void print(T1 value, T... args)
{
std::cout << value << std::endl;
print(args...);
}
线程相关
C++11中引入了线程库,原子操作库,线程本地存储等,其中原子库std::atomic,首次明确了内存模型,进行性能优化时,可以考虑使用。线程本地存储使用thread_local修饰保证线程本地变量。
指针空值nullptr
nullptr特指指针空值,彻底和老式的NULL其实就是0决裂,可以直接使用nullptr给指针类型置空,nullptr和NULL之间不能直接转换,nullptr是nullptr_t类型,所有的nullptr_t类型都是一样的。
匿名函数
lambda函数即匿名函数,C++11中一大亮点,以前使用STL中的算法时,需要定义一个仿函数或者是一个全局函数,极不方便,有了匿名函数可以直接在调用地方书写函数,方便又清晰。[]中是引用列表,“=”表示值传递父作用域变量,“&”表示引用传递父作用域,默认lambda是const类型,不允许修改传入参数值。
auto fn1 = [=](int a) -> int{ return a; };
auto fn2 = [&]() -> int{ return 2; };
std::cout << fn1(1) << std::endl;
std::cout << fn2() << std::endl;
对齐方式
C++98中并没有明确对齐方式,所有的对齐都是编译器隐式完成,也可以使用编译提供的特性,指定对齐方式,例如 #pragma pack(n),特别是struct类型在跨网络传输时要特别注意对齐方式,C++11中使用alignas关键字指定对齐方式,alignof计算变量对齐方式。
struct alignas(int) foo
{
char c;
int a;
};
std::cout << alignof(foo) << std::endl;
Unicode支持
C++定义原生支持Unicode编码,char16_t用于存储UTF-16编码,char32_t用于存储UTF-32编码;前缀表示u8表示UTF-8编码,u表示UTF-16编码,U表示为UTF-32编码。R前缀支持原生字符串输出,并不会进行转义,u8R,uR,UR分别支持UTF-8,UTF-16,UTF-32原生字符串。
C++11笔记的更多相关文章
- C++ 11 笔记 (三) : auto
我真的不是标题党... 虽然大一上学期学C语言基础时就学了auto关键字了,而且还是跟static和register两个关键字打包学的,但是.. 猜的没错,C++11这货又给auto加新功能了,在 C ...
- C++ 11 笔记 (六) : 随机数
以前生成一个随机数都是这样: srand(time(NULL)); rand(); 在C++11中,标准库中增加了随机数引擎 std::default_random_engine 这个好东西,然后我们 ...
- C++ 11 笔记 (五) : std::thread
这真是一个巨大的话题.我猜记录完善绝B需要一本书的容量. 所以..我只是略有了解,等以后用的深入了再慢慢补充吧. C++写多线程真是一个痛苦的事情,当初用过C语言的CreateThread,见过boo ...
- C++ 11 笔记 (二) : for循环
首先肯定的是,我不是标题党.. C++11的for循环确实有跟C++98不一样的地方,还是先上代码: , , , , }; for (int x : test_arr) { std::cout < ...
- C++ 11 笔记 (一) : lambda
时至今日都是我咎由自取,错就是错,与任何人无关.掉进C++98的各种坑里无法自拔的抖M感,让我选择了华丽丽的无视C++11,导致今日面对开源的代码到各种看不懂的地步,一入C++深似海,我今天愿意承担一 ...
- C++11 笔记
5.重载运算符 本质上是一个函数. 函数名为operator(+-*/--) 如果一个运算符是成员函数,其左侧运算对象就绑定到隐式的this参数上. a.拷贝赋值运算符 例如: class Foo { ...
- C++11笔记<一>
目录: 1.std::share_ptr智能指针: 2.std::tr1::function模板类: 3.stringstream: 4.set/vector/map: 5.static_cast&l ...
- 斯坦福iOS7公开课11笔记及演示Demo&访问HTTPS链接下载数据
这一节主要介绍UITableView以及iPad,Demo为从Flicker下载图片并显示,但是实际过程中发现需要FQ并使用HTTPS连接,所以这次用了两个Demo,一个是课程中的Demo,另一个是简 ...
- C++ 11 笔记 (四) : std::bind
std::bind 接受一个可调用的对象,一般就是函数呗.. 还是先上代码: void func(int x, int y, int z) { std::cout << "hel ...
随机推荐
- JsRender系列demo(3)-自定义容器
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...
- EXPRESS.JS再出发
那个那个MEAN的书,看得七七八八,有了大概,现在就要一样一样的加深记忆啦.. EXPRSS.JS的东东,网上有现成入门书籍: 第一期代码测试: var express = require('expr ...
- docker: "build" requires 1 argument. See 'docker build --help'.
http://bbs.csdn.net/topics/391040030 docker build --tag="ouruser/sinatra:v3" -<Dockerf ...
- 缓存初解(三)---Spring3.0基于注解的缓存配置+Ehcache和OScache
本文将构建一个普通工程来说明spring注解缓存的使用方式,关于如何在web应用中使用注解缓存,请参见: Spring基于注解的缓存配置--web应用实例 一.简介 在spring的modules包中 ...
- 一个简单的将GUI程序的log信息输出到关联的Console窗口中(AllocConsole SetConsoleTitle WriteConsole 最后用ShowWindow(GetConsoleWindow)进行显示)
// .h 文件 #pragma once class CConsoleDump { public: explicit CConsoleDump(LPCTSTR lpszWindowTitle = N ...
- *windows下安装以及配置nginx
1.从nginx官网下载相应的安装包. http://nginx.org/
- Django模型修改及数据迁移
Migrations Django中对Model进行修改是件麻烦的事情,syncdb命令仅仅创建数据库里还没有的表,它并不对已存在的数据表进行同步修改,也不处理数据模型的删除. 如果你新增或修改数据模 ...
- Java API —— Set接口 & HashSet类 & LinkedHashSet类
1.Set接口 1)Set接口概述 一个不包含重复元素的 collection,无序(存储顺序和取出顺序不一致),唯一. (List有序,即存储顺序和取出顺序一致,可重复) ...
- math模块及使用方式
在数学之中,除了加减乘除四则运算之外——这是小学数学——还有其它更多的运算,比如乘方.开方.对数运算等等,要实现这些运算,需要用到 Python 中的一个模块:Math 模块(module)是 Pyt ...
- POJ 1808 Quadratic Residues(平方剩余相关)
题目链接:http://poj.org/problem?id=1808 题意:如下.对于素数p,若存在x使得x^2%p=a,则其值为1.否则为-1.现在给出a.p,计算其值. 思路: 若a为正数则利用 ...