在了解cocos2d-x引擎的时候,或者对项目的底层做一些修改的时候,经常会碰到C++ 11特性相关的东西。
所以对C++ 11的一些特性进行了记录和汇总。
更多的内容参考的是微信公众号: 程序喵大人(微信号:chengxumiaodaren)
在此感谢原作者的分享。
主要的C++11特性有:
nullptr,auto,delctype
类型推导,for范围循环
等静态断言
,外部模版
等枚举类型安全
等多线程
,并行编程
等继承构造
,委托构造
,列表初始化
相关lambda表达式
等简介 | 关键字相关 |
---|---|
基础 | nullptr, long long, auto, decltype, char16_t, char32_t, constexptr, static_assert, 随机数 |
类 | enum, sizeof, 委托构造,继承构造, override, final, , default, delete, explicit |
STL | std::array, std::tuple, std::forward_list, std::unordered_map, std::unordered_set, cbegin/cend |
智能指针 | std::shared_ptr, std::weak_ptr, std::unique_ptr |
线程 | std::thread, std::mutex, std::lock, std:atomic, std::call_once, std::future, std::condition_variable, volatile, async |
其他 | std::function, std::bind, lamada表达式 |
主要用于代替NULL
, NULL
实质上就是int
整形的0,不算是指针。新增的类型:
typedef decltype(nullptr) nullptr_t;
这样使用,代码安全性更高。
用于表示Unicode字符,被用于做国际化和多语言文本处理。分别表示16位和32位。
// 使用前缀'u'来表示char16_t类型的字符
char16_t myChar = u'A';
// 使用前缀'U'来表示char32_t类型的字符
char32_t myChar = U'';
对整数类型的拓展,支持更大范围的数字
long long int value = 100000;
自动推导变量类型,可用于简化代码,增强可读性。
示例1:
// 自动推导a为int类型
auto a = 10;
int i = 10;
// 自动推导b为int类型
auto b = i;
int i = 10;
// 自动推导a为int, b为i的引用, c为i的指针
auto a = i, &b = i, *c = &i;
示例2:
vector<int> vec;
// old
for(vector<int>::iterator iter = vec.begin(); iter != vec.end(); iter++) {
cout << *iter << endl;
}
// new
for (auto iter = vec.begin(); iter != vec.end(); iter++) {
cout << *iter << endl;
}
使用auto
注意:
必须初始化,否则无法推导
不能用做函数参数
若一行定义多个变量,不能产生二义性,否则编译报错
注意精度
不能推导模版参数
// 二义性 error: declaration of ‘auto a’ has no initializer
auto a, b = 1, 1.0;
// 精度相关,越界了
unsigned int a = 4294967295; // unsigned int 能够存储的最大数据
unsigned int b = 1;
auto c = a+b;
cout << c << endl; // 输出c为0
与auto
类似,但用于推导表达式。 注意:仅是推导,不会进行运算
int func() {return 0;}
decltype(func()) i; // 推导i为int类型
int x = 0;
decltype(x) y; // 推导y是int类型
decltype(x + y) z; // 推导z是int类型
用于简化for循环程序,在STL迭代器中遍历较为明显,比如:
vector<int> vec;
// before
for (auto iter = vec.begin(); iter != vec.end(); iter++) {
cout << *iter << endl;
}
// new
for(int i:vec) {
cout << i << endl;
}
它同const
一样,都用于定义常量, 但:const
在运行时计算值, 而constexpr
在编译时计算值。
在编译期间计算,避免了运行时的计算开销,提高了程序的执行效率。
// 阶乘
constexpr int factorial(int n) {
return n == 0 ? 1 : n * factorial(n - 1);
}
// 编译期计算5的阶乘作为数组大小
int arr[factorial(5)];
因在编译时计算,故此使用constexpr
注意:
constexpr
变量virtual
成员方法在C++ 11增加constexpr
后,const
可以理解为只读属性的变量,而真正的常量用constexprt
表示。
新增的关键字,静态断言。可用于在编译时对表达式检查错误。
// expression 断言的表达式,如果为false,则编译错误
// message 错误的信息字符串
static_assert(expression, message);
对随机数进行了拓展,在丰富原有功能的同时,引入了新的随机数库
, 并新增了一些随机数生成器和分布函数相关。
新增的随机数生成器(生成随机数序列), 主要有:
std::mt19937
std::minstd_rand
std::linear_congruential_engine
新增的随机分布函数:
简单的实例:
#include
#include
#include
using namespace std;
int main() {
std::default_radom_engine random(time(nullptr));
// 整数均匀分布
std::uniform_int_distribution<int> int_dis(0, 100);
// 浮点数均匀分布
std::uniform_read_distribution<float> float_dis(0.0f, 1.0f);
for (int i=0; i <10; ++i) {
cout << int_dis(random) << float_dis(random)<< endl;
}
return 0;
}
C++ 11 对类相关的主要修改有:
enum
增加类作用域sizeof
获取类成员对象的大小,不需要再定义对象后计算大小了overide, final, default, delete, explicit
等。下面将列举各个示例相关:
enum
枚举类型,增加了类作用域,以及可设置类型,默认为int// before
enum AColor {
kRed, kGreen, kBlue
};
enum BColor {
kWhite, kBlack, kYellow
};
if(kRed == kWhite) {}
// new
enum class AColor: int {
kRed, kGreen, kBlue
};
enum class BColor: int {
kWhite, kBlack, kYellow
};
// 不同作用域的比较,会导致编译失败,消除潜在的bug
if(AColor::kRed == BColor::kWhite) {}
struct A {
int data[10];
int a;
};
// before
int main() {
A a;
cout << "size " << sizeof(a.data) << endl;
return 0;
}
// new
int main() {
cout << "size " << sizeof(A::data) << endl;
return 0;
}
// before
class A {
A(){}
A(int a) { a_ = a; }
A(int a, int b) { // 好麻烦
a_ = a;
b_ = b;
}
A(int a, int b, int c) { // 好麻烦
a_ = a;
b_ = b;
c_ = c;
}
int a_, b_, c_;
};
// new
class A {
A(){}
A(int a) { a_ = a; }
A(int a, int b) : A(a) { b_ = b; }
A(int a, int b, int c) : A(a, b) { c_ = c; }
int a_, b_, c_;
};
class Base {
Base() {}
Base(int a) { a_ = a; }
Base(int a, int b) : Base(a) { b_ = b; }
Base(int a, int b, int c) : Base(a, b) { c_ = c; }
int a_, b_, c_;
};
// before
class Derived : Base {
Derived() {}
Derived(int a) : Base(a) {} // 好麻烦
Derived(int a, int b) : Base(a, b) {} // 好麻烦
Derived(int a, int b, int c) : Base(a, b, c) {} // 好麻烦
};
// new
class Derived : Base {
using Base::Base;
};
int main() {
Derived a(1, 2, 3);
return 0;
}
// overide主要用于避免程序在重写基类函数时无意产生的错误
// 如果基类中没有声明,在子类中编写的时候就会报错
class Base {
virtual void func() {
cout << "base" << endl;
}
};
class Derived : public Base{
void func() override { // 确保func被重写
cout << "derived" << endl;
}
// error,基类没有fu(),不可以被重写
void fu() override {}
};
struct Base final {
virtual void func() {
cout << "base" << endl;
}
};
struct Derived : public Base{ // 编译失败,final修饰的类不可以被继承
void func() override {
cout << "derived" << endl;
}
};
class CC_STUDIO_DLL ILocalizationManager {
public:
// 在函数声明后加上“=default;”,就可将该函数声明为 defaulted 函数
// 编译器将为显式声明的 defaulted 函数自动生成函数体
virtual ~ILocalizationManager() = default;
virtual bool initLanguageData(std::string file) = 0;
virtual std::string getLocalizationString(std::string key) = 0;
};
delete 如果没有定义特殊成员函数,那么编译器在需要特殊成员函数时候会隐式自动生成一个默认的特殊成员函数,例如拷贝构造函数或者拷贝赋值操作符
我们有时候想禁止对象的拷贝与赋值,可以使用delete
修饰
class A {
A() = default;
A(const A&) = delete;
A& operator=(const A&) = delete;
int a;
A(int i) { a = i; }
};
int main() {
A a1;
A a2 = a1; // 错误,拷贝构造函数被禁用
A a3;
a3 = a1; // 错误,拷贝赋值操作符被禁用
}
class A {
explicit A(int value) {
cout << "value" << endl;
}
};
int main() {
A a = 1; // error,不可以隐式转换
A aa(2); // ok
return 0;
}
STL实现了许多通用的数据结构及处理这些结构的算法。包含三大组件:
deque, list, vector, map
等C++11新增的一些容器有:
std::array
替代数组的固定大小容器std::forward_list
新增的单项链表,比std::list
性能更高std::unordered_map
新增的使用哈希表存储的map容器std::unordered_set
新增的使用哈希表存储的set容器std::tuple
新增的元组一些简单的示例:
#include
#include // 声明头文件
int main()
{
std::array<int, 4> data = {1, 2, 3, 4};
for(auto value: data) {
std::cout << value << std::endl;
}
std::cout << sizeof(data) << std::endl;
return 0;
}
std::list
相比为单项链表,运行时比std::list
有更好的性能。#include
int main()
{
std::forward_list<int> numbers = {1,2,3,4,5,4,4};
std::cout << "numbers:" << std::endl;
for (auto number : numbers){
std::cout << number << std::endl;
}
numbers.remove(4);
std::cout << "numbers after remove:" << std::endl;
for (auto number : numbers){
std::cout << number << std::endl;
}
return 0;
}
std::map
使用类似,采用的是哈希表方式/*
std::map 内部采用红黑树(一种近似平衡的二叉树)存储,为有序容器
std::unordered_map 内部采用哈希表存储,无序容器
unordered_map优点:查找元素快,内存占用低,插入删除快
*/
#include
#include
#include // 头文件引用
using namespace std;
int main() {
std::unordered_map<std::string, std::string> mymap ={
{ "house","maison" },
{ "apple","pomme" },
{ "tree","arbre" },
{ "book","livre" },
{ "door","porte" },
{ "grapefruit","pamplemousse" }
};
unsigned n = mymap.bucket_count();
cout << "mymap has " << n << " buckets.\n";
for (int i = 0; i<n; ++i) {
cout << "bucket #" << i << " contains: ";
for (auto it = mymap.begin(i); it != mymap.end(i); ++it)
cout << "[" << it->first << ":" << it->second << "] ";
}
return 0;
}
#include
#include
#include
#include
using namespace std;
int main()
{
std::unordered_set<int> unorder_set;
unorder_set.insert(7);
unorder_set.insert(5);
unorder_set.insert(3);
unorder_set.insert(4);
unorder_set.insert(6);
std::cout << "unorder_set:" << std::endl;
for (auto itor : unorder_set) {
cout << itor << endl;
}
std::set<int> set;
set.insert(7);
set.insert(5);
set.insert(3);
set.insert(4);
set.insert(6);
std::cout << "set:" << std::endl;
for (auto itor : set) {
std::cout << itor << std::endl;
}
}
智能指针是一个类,用于管理申请的空间在函数结束后忘记释放,导致内存泄漏的问题。
在超出类的作用域后,它会自动调用析构函数。主要有:
shard_ptr
搭配,不会增加引用计数,用于避免循环引用(a对象有b, b对象有a)。它对对象的引用是弱引用,可以绑定到shared_ptr, 但不能增加对象的引用计数可调用对象的函数封装器, 可调用对象需要满足如下条件:
operator()
成员函数的类对象,lambda
表达式std::bind
表达式或其他函数对象std::function
的实例可以存储、复制和调用任何可调用对象,存储的可调用对象称为std::function的目标。
若std::function不含目标,则称它为空,调用空的std::function的目标会抛出std::bad_function_call
异常
std::function<void (cocos2d::Ref *)> arg3;
将可调用对象和参数一起绑定,绑定后的结果使用std::function
进行保存。
并延迟调用到任何我们需要的时候, 通常有两大作用:
std::function
供调用std::placeholders
MenuItemFont * MenuItemFont::create(const std::string& value, Ref* target, SEL_MenuHandler selector) {
MenuItemFont *ret = new (std::nothrow) MenuItemFont();
ret->initWithString(value, std::bind(selector, target, std::placeholders::_1));
ret->autorelease();
return ret;
}
用以定义了一个匿名函数,可以捕获一定范围的变量在函数内部使用, 语法形式:
/*
func 可以当作lambda表达式的名字,作为一个函数使用,
capture 捕获列表
params 参数表
opt 是函数选项(mutable之类)
ret 是返回值类型
func_body 是函数体
*/
auto func = [capture] (params) opt -> ret { func_body; };
// 例子
auto func1 = [](int a) -> int { return a + 1; };
auto func2 = [](int a) { return a + 2; };
cout << func1(1) << " " << func2(2) << endl;
lambda表达式允许捕获一定范围内的变量:
[]不捕获任何变量
[&]引用捕获,捕获外部作用域所有变量,在函数体内当作引用使用
[=]值捕获,捕获外部作用域所有变量,在函数内内有个副本使用
[=, &a]值捕获外部作用域所有变量,按引用捕获a变量
[a]只值捕获a变量,不捕获其它变量
[this]捕获当前类中的this指针
C++ 11的特性还在学习使用中,更多内容参考大神:程序喵大人(微信号:chengxumiaodaren)
祝大家学习生活愉快!