自动推导变量数据类型,用于从初始化表达式中推断出变量的数据类型。类型确定后,不能重复初始化。
// testC++11.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include
#include
#include
#include
using namespace std;
struct testStruct
{
int a;
int b;
};
int main()
{
// auto
cout << "Hello World!\n";
auto a = 5; //int类型
//a = "ding"; //错误,不能重复初始化,a已推到出int类型
auto b = "testing"; //不是string类型
string c = "testing";
//auto d = b.c_str();
auto e = c; //string类型
cout << e.c_str() << endl;
char* f = new char[5];
auto g = f; //char*类型
f[5] = 0;
g[5] = 0;
f = NULL;
g = NULL;
auto* h = new testStruct; //自定义testStruct类型
h->a = 5;
vector<int> i;
vector<vector<int>> j;
map<int, string> k;
k[1] = "aaa";
map<int, string>::iterator it = k.begin();
auto l = i; //vector类型
auto m = j; //vector>类型
auto n = k; //map
//auto ll = i, mm = j, nn = k; //错误写法,编译失败
auto ll = 5, mm = 6, nn = 7; //正确写法,在声明符列表中,auto必须始终推到为同一类型
auto iter = k.begin(); //map::iterator类型
double a2 = 3.144;
const auto a3 = a2; //const double
auto a4 = a2; //double
volatile int c2 = 3;
auto c3 = c2; //int
return 0;
}
for循环迭代差异:
// testC++11.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include
#include
using namespace std;
int main()
{
//C++98
vector<int> vec(8, 1);
cout << "C++98 range for:" << endl;
for (vector<int>::iterator it = vec.begin(); it != vec.end(); it++)
{
cout << *it << endl;
}
//C++11
cout << "C++11 range for:" << endl;
for (auto d : vec) //d中存储的是vec中的值
{
d = 2; //不会改变vector中的值,d只是一个临时变量;
}
for (auto d : vec) //d中存储的是vec中的值
{
cout << d << endl;
}
for (auto &d : vec)
{
d = 6; //可以改变vector中的值
}
for (auto d : vec)
{
cout << d << endl;
}
//数组for_each
char arr[] = {'a','b','c','d'};
for (auto &d : arr)
{
d -= 32;
}
for (auto d : arr)
{
cout << d << endl;
}
//遍历二维数组,注意迭代变量row必须为引用。如果你想用 range for 的方法,来遍历更高维的数组 (dim > 2),那么你只需要:除了最内层循环之外,其他所有外层循环都加入 '&' 即可。
int array2[5][5] = {0};
for (auto &row : array2)
for (auto col : row)
cout << col << endl;
return 0;
}
通过一个变量或表达式得到变量的数据类型,并不会真正的执行表达式;
// testC++11.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include
#include
#include
#include
using namespace std;
int func(int a)
{
cout << "this is func, int type param" << endl;
return a;
}
string func(string b)
{
cout << "this is func, string type param" << endl;
return "this is func, string type param";
}
void func()
{
cout << "this is func, without return value" << endl;
}
int main()
{
// decltype
map<int, int> aa;
aa[5] = 6;
decltype(aa) bb = aa; //map类型
bb[1] = 2;
map<int, int>::iterator cc = aa.begin();
decltype(cc) dd = cc; //map::iterator类型
decltype(func(5)) ee = 5; //int类型,并不会执行func(5)
decltype(func("5")) ff = "5"; //string类型,并不会执行func("5")
// decltype(func()) gg = 6; //编译错误,非法使用void类型
return 0;
}
nullptr是一个表示空指针的标识。NULL只是一个定义为常整数0的宏,而nullptr是C++11的一个关键字,一个內建的标识符。
// testC++11.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include
#include
using namespace std;
void fun(int a)
{
cout << "call function: [int]\n" << endl;
}
void fun(int *a)
{
cout << "call function: [int*]\n" << endl;
}
int main()
{
fun(NULL); //调用void fun(int a)
fun(nullptr); //调用void fun(int *a)
int* p = NULL;
fun(p); //调用void fun(int *a)
return 0;
}
final使用方法有以下三种:
C++11中的final不能修饰变量,修饰方法和类与java中final功能相同。
class Base final //final修饰类
{
public:
int a;
//final int b; //final不能修饰变量
virtual void func() final; //final修改函数,不能被子类重写
};
class Last final : Base //Base不能被继承
{
};
允许派生类显示的注明它将使用那个成员函数改写基类的虚函数,必须是virtual修饰的虚函数。
与java中的@override功能类似,标识重写(覆盖)。
struct B
{
virtual void f1(int) const;
virtual void f2();
void f3();
};
struct D1 : public B
{
void f1(int) const override; //ok
void f2(int) override; //error,B中没有形如f2(int)的函数
void f3() override; //error,f3不是虚函数
void f4() override; //error,B中无f4函数
};
对于 C++ 的类,如果程序员没有为其定义特殊成员函数,那么在需要用到某个特殊成员函数的时候,编译器会隐式的自动生成一个默认的特殊成员函数,比如拷贝构造函数,或者拷贝赋值操作符。
C++11允许我们使用=default来要求编译器生成一个默认构造函数,也允许我们使用=delete来告诉编译器不要为我们生成某个默认函数
class B
{
B() = default; //显示声明使用默认构造函数
B(const B&) = delete; //禁止使用类对象之间的拷贝
~B() = default; //显示声明使用默认析构函数
B& operator=(const B&) = delete; //禁止使用类对象之间的赋值
B(int a);
};
lambda的基础语法定义如下:
[capture](parameters) mutable ->return-type{statement}
[capture]:捕获列表。它总是出现在lambda函数的开始位置。在编译器看来[]是lambda的引出符号,编译器正式通过它来判断接下来的代码是否是lambda函数。捕获列表能够捕捉当前上下文中的变量供给lambda函数使用。具体的capture列表中的语法,下面还会详细讲述。
(parameters):参数列表。它跟一般函数的参数列表一样,使用规则也相同。在lambda中,如果不需要传入参数,可以省略。
mutable:修饰符。默认情况下,lambda函数总是一个const函数,mutable可以取消它的常量属性。显示指定mutable修饰符的时候,参数列表不能省略。
->return-type:返回值类型。->这个同C++11新引入的追踪返回值类型的声明是一致的,语法也是一致的。不同的是,处于方便,lambda函数在没有返回值的情况下,可以省略掉(在某些编译器可以推导出返回值类型的情况亦可省略)。
{statement}:函数体。与一般函数的函数体一致,额外可以使用捕获列表中捕获的变量。
直观感受下lambda的使用:
// testC++11.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include
using namespace std;
int main()
{
auto f = []() {cout << "hello world!" << endl; };
f(); //hello world!
int a = 123;
auto f1 = [a] { cout << a << endl; };
f1(); //123
auto f2 = [&a] {cout << a << endl; };
a = 789;
f2(); //789
//隐式捕获:让编译器根据函数体中的代码来推断需要捕获哪些变量
auto f3 = [=] {cout << a << endl; };
f3(); //789
auto f4 = [&] {cout << a << endl; };
a = 990;
f4(); //990
auto f5 = [](int a, int b)->int {return a + b; };
printf("%d\n", f5(1, 2)); //3
return 0;
}
lambda函数相较普通函数调用最便捷之处就是其捕获列表,它可以通过值传递捕获或者引用传递方式捕获,直接在函数体内访问到上下文(一个代码块内)的变量。
如果是普通函数的话,这些都要以参数形式传递进去,使代码十分冗长。那么捕获列表的具体语法可以归纳如下:
[a]表示值传递捕获变量a(多个参数可以用逗号分隔)
[=]表示值传递捕获上下文所有变量
[&a]表示引用传递捕获变量a
[&]表示引用传递捕获上下文所有变量
[this]表示值传递捕获当前的this指针
[=, &a, &b]表示值传递捕获上下文所有变量,但是a、b变量以引用传递方式捕获。
[&, a, this]表示引用传递捕获上下文所有变量,但是a和this指针以值传递方式捕获
char c = 'a';
float d = 1.11f;
foo([=](int param)->void
{
std::cout << "lambda param is :"<< param <<std::endl;
std::cout << "lambda cap is :" << c << std::endl;
std::cout << "lambda cap2 is :" << d << std::endl;
}, 123);
是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝。
// testC++11.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include
#include
#include
#include
int main()
{
std::string str = "Hello";
std::vector<std::string> v;
//调用常规的拷贝构造函数,新建字符数组,拷贝数据
v.push_back(str);
std::cout << "After copy, str is \"" << str << "\"\n"; //After move, str is "Hello"
//调用移动构造函数,掏空str,掏空后,最好不要使用str
v.push_back(std::move(str));
std::cout << "After move, str is \"" << str << "\"\n"; //After move, str is ""
std::cout << "The contents of the vector are \"" << v[0]
<< "\", \"" << v[1] << "\"\n"; //The contents of the vector are "Hello", "Hello"
}
使用 std::array保存在栈内存中,相比堆内存中的 std::vector,我们就能够灵活的访问这里面的元素,从而获得更高的性能;同时正式由于其堆内存存储的特性,有些时候我们还需要自己负责释放这些资源。
使用std::array能够让代码变得更加现代,且封装了一些操作函数,同时还能够友好的使用标准库中的容器算法等等,比如 std::sort。
std::array 会在编译时创建一个固定大小的数组,std::array 不能够被隐式的转换成指针,使用 std::array 很简单,只需指定其类型和大小即可:
// testC++11.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include
#include
#include
void foo(int* p)
{
}
int main()
{
std::array<int, 4> arr = {4,3,1,2};
foo(&arr[0]); //OK
foo(arr.data()); //OK
//foo(arr); //wrong
std::sort(arr.begin(), arr.end()); //排序
return 0;
}
std::forward_list 使用单向链表进行实现,提供了 O(1) 复杂度的元素插入,不支持快速随机访问(这也是链表的特点),也是标准库容器中唯一一个不提供 size() 方法的容器。当不需要双向迭代时,具有比 std::list 更高的空间利用率。
// testC++11.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include
#include
#include
#include
#include
int main()
{
std::forward_list<int> list1 = { 1, 2, 3, 4 };
//从前面向foo1容器中添加数据,注意不支持push_back
list1.pop_front(); //删除链表第一个元素
list1.remove(3); //删除链表值为3的节点
list1.push_front(2);
list1.push_front(1);
list1.push_front(14);
list1.push_front(17);
list1.sort();
for (auto &n : list1)
{
if (n == 17)
n = 19;
}
for (const auto &n : list1)
{
std::cout << n << std::endl; //1 2 2 4 14 19
}
return 0;
}
无序容器中的元素是不进行排序的,内部通过 Hash 表实现,插入和搜索元素的平均复杂度为 O(constant),在不关心容器内部元素顺序时,能够获得显著的性能提升。
C++11 引入了两组无序容器:std::unordered_map/std::unordered_multimap 和 std::unordered_set/std::unordered_multiset。
下面给出unordered_map和unordered_set的使用方法。
// testC++11.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include
#include
#include
#include
#include
#include
void foo(int* p)
{
}
int main()
{
//unordered_map usage
std::unordered_map<std::string, int> um = { {"2",2},{"1",1},{"3",3} };
//遍历
for (const auto &n : um)
{
std::cout << "key:" << n.first << " value:" << n.second << std::endl;
}
std::cout << "value:" << um["1"] << std::endl;
//unordered_set usage
std::unordered_set<int> us = { 2,3,4,1};
//遍历
for (const auto &n : us)
{
std::cout << "value:" << n << std::endl;
}
std::cout << "value:" << us.count(9) << std::endl; //判断一个数是否在集合内,1存在0不存在
std::cout << "value:" << *us.find(1) << std::endl; //查找一个特定的数是否在集合内,找到就返回该数的迭代器位置
return 0;
}
shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,删除所指向的堆内存。赋值非原子操作,多线程使用需加锁。
// testC++11.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include
#include
#include
int main()
{
//auto ptr = std::make_shared(10);
std::shared_ptr<int> ptr(new int(10));
std::shared_ptr<int> ptrC(ptr);
auto ptr2 = ptr;
{
auto ptr3 = ptr2;
std::cout << "pointer1.use_count() = " << ptr.use_count() << std::endl; //4
std::cout << "pointer2.use_count() = " << ptr2.use_count() << std::endl; //4
}
std::cout << "pointer1.use_count() = " << ptr.use_count() << std::endl; //3
std::cout << "pointer2.use_count() = " << ptr2.use_count() << std::endl; //3
int *p = ptr.get(); //获取原始指针
std::cout << "pointer1.use_count() = " << ptr.use_count() << std::endl; //3
std::cout << "pointer2.use_count() = " << ptr2.use_count() << std::endl; //3
return 0;
}
一种独占的智能指针,它禁止其他智能指针与其共享同一个对象,从而保证代码的安全。
// testC++11.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include
#include
#include
int main()
{
std::unique_ptr<int> ptr(new int(10));
//auto ptr2 = ptr; //非法
//虽说unique_ptr是不可复制的,但我们可以使用std::move将其独占权转移到其他的unique_ptr
auto ptr2(std::move(ptr));
std::cout << *ptr2 << std::endl;
return 0;
}
weak_ptr是为了配合shared_ptr而引入的一种智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。
// testC++11.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include
#include
#include
#include
#include
using namespace std;
#include
#include
using namespace std;
class A;
class B;
class A {
public:
A() {
cout << "A !" << endl;
}
~A() {
cout << "~~~~~A !" << endl;
}
void setBB(shared_ptr<B> val) {
bb = val;
}
private:
//shared_ptr bb;
weak_ptr<B> bb; //用循环引用的指针之一为weak_ptr,来解决循环引用
};
class B {
public:
B() {
cout << "B !" << endl;
}
~B() {
cout << "~~~~~~B !" << endl;
}
void setAA(shared_ptr<A> val) {
aa = val;
}
private:
shared_ptr<A> aa;
};
int main()
{
shared_ptr<A> aa(new A());
shared_ptr<B> bb(new B());
aa->setBB(bb);
bb->setAA(aa);
return 0;
}
参考链接:
https://www.cnblogs.com/skyfsm/p/9038814.html
https://www.cnblogs.com/lenmom/p/9198126.html
https://www.jianshu.com/p/5ea6e6feeaf3
https://www.jianshu.com/p/234b818f289a