目录
环境准备
被弃用的特性
常量字符串赋值需要使用const char*
与C的兼容性
语言可用性的强化
类型推导
区间迭代
列表初始化
模板增强
面对对象增强
语言运行期的强化
Lambda表达式
std::function
std::bind/std::placeholder
右值引用
新增容器
智能指针和引用计数
RAII与引用计数
std::shared_ptr
std::unique_ptr
std::shared_ptr仍然无法解决的问题
在IDE工具中选择编译器,加入编译命令 -std=c++11
(弃用暗示避免使用,但仍是标准库的一部分)
char* str = "Hello world!";
这里的char*和const char*都是指向一个常量字符串,如果有另外一个char* str2指向的常量字符串和str一模一样,那么其实str和str2也的地址是完全相同的,这有点类似java的常量池。
auto_ptr
被弃用,应使用 unique_ptr
。- #include<iostream>
- #include<memory>
- using namespace std;
- class A
- {
- public:
- A()
- {
- cout << "A()";
- }
- ~A()
- {
- cout << "~A()";
- }
- };
- int main()
- {
- auto_ptr<A> global;
- {
- cout << "Test1\n";
- auto_ptr<A> a(new A());
- }
- {
- cout << "\nTest2\n";
- A* b = new A();
- auto_ptr<A> a(b);
- global.reset(b);
- auto_ptr<A> c(b);
- }
- {
- cout << "\nTest3\n";
- A*b = new A[5];
- auto_ptr<A> a(b);
- }
- {
- cout << "\nTest3\n";
- unique_ptr<A> a(new A[5]);
- }
- cout << "\nEnd\n";
- system("pause");
- }
auto_ptr和unique_ptr的构造函数都声明为explicit,为了避免不知情的情况下出现的错误,不能存在隐式转换。
- std::auto_ptr<A> a = new A(); //编译错误
- std::auto_ptr<A> a(new A()); //编译成功
关于explicit 更详细的解释 https://www.cnblogs.com/cutepig/archive/2009/01/14/1375917.html
auto_ptr和unique_ptr一样不能处理数组,因为它们析构的时候,都只是delete ptr,而不是delete []ptr。
register
关键字被弃用。static_cast
、reinterpret_cast
、const_cast
来进行类型转换C++不是C的一个超集,在编写C++时,也应该尽可能地避免诸如void *之类的程序风格,而在不得不使用C时,应该使用extern "C"这种特性,将C语言的代码与C++代码进行分离编译,再统一链接这种做法。
https://www.cnblogs.com/skynet/archive/2010/07/10/1774964.html
nullptr用来区分空指针和0,和传统C++会把NULL、0视为同一种东西,这会在C++中重载特性中发生混乱。
constexpr用来修饰一段代码,说明该函数在编译时便一定是个常数,便于编译器优化。
- // constexpr 声明factorial可以参与编译期的运算
- constexpr int factorial(int n)
- {
- return n <= 1? 1 : (n * factorial(n - 1));
- }
- int main()
- {
- std::cout << "4! = " ;
- constN<factorial(4)> out1; // computed at compile time
- volatile int k = 8; // disallow optimization using volatile
- std::cout << k << "! = " << factorial(k) << '\n'; // computed at run time
- }
C++11引入了auto和decltype
- auto x = 1;
- auto y = 2;
- decltype(x+y) z;
- //尾返回类型
- template<typename T, typename U>
- auto add(T x, U y) -> decltype(x+y) {
- return x+y;
- }
- //C++直接支持
- template<typename T, typename U>
- auto add(T x, U y) {
- return x+y;
- }
- int array[] = {1,2,3,4,5};
- for(auto &x : array) {
- std::cout << x << std::endl;
- }
- //std::vector
- //原来
- int array[] = {1,2,3,4,5};
- for(auto &x : array) {
- std::cout << x << std::endl;
- }
- //现在
- // & 启用了引用, 如果没有则对 arr 中的元素只能读取不能修改
- for(auto &i : arr) {
- std::cout << i << std::endl;
- }
可以用一种统一的方式对数组,结构体,类和所有STL容器进行初始化。
- #include<iostream>
- #include<map>
- #include<list>
- #include<vector>
- using namespace std;
- struct Foo
- {
- int x;
- int y;
- Foo(int, int){ cout << "Foo construction"; }
- };
- int main()
- {
- int arr[] = { 1, 2, 3, 4, 5 };
- std::map < int, int > map_t { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
- std::list<std::string> list_str{ "hello", "world", "china" };
- std::vector<double> vec_d { 0.0,0.1,0.2,0.3,0.4,0.5};
- Foo foo{1,2};
- for(auto& x : vec_d)
- {
- std::cout<<x<<" ";
- }
- std::cout<<"\n";
- }
也可以列表初始化构造函数,列表还可以作为普通函数的形参。
- #include <initializer_list>
- class Magic {
- public:
- Magic(std::initializer_list<int> list) {}
- };
- Magic magic = {1,2,3,4,5};
- std::vector<int> v = {1, 2, 3, 4}
- void func(std::initializer_list<int> list) {
- return;
- }
- func({1,2,3});
统一的语法来初始化任意对象
- struct A {
- int a;
- float b;
- };
- struct B {
- B(int _a, float _b): a(_a), b(_b) {}
- private:
- int a;
- float b;
- };
- A a {1, 1.1}; // 统一的初始化语法
- B b {2, 2.2};
在传统 C++ 的编译器中,>>
一律被当做右移运算符来进行处理。但实际上我们很容易就写出了嵌套模板的代码:
std::vector<std::vector<int>> wow;
这在传统C++编译器下是不能够被编译的,而 C++11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。
在了解类型别名模板之前,需要理解『模板』和『类型』之间的不同。仔细体会这句话:模板是用来产生类型的。在传统 C++中,typedef
可以为类型定义一个新的名称,但是却没有办法为模板定义一个新的名称。因为,模板不是类型。例如:
- template< typename T, typename U, int value>
- class SuckType {
- public:
- T a;
- U b;
- SuckType():a(value),b(value){}
- };
- template< typename U>
- typedef SuckType<std::vector<int>, U, 1> NewType; // 不合法
C++11 使用 using
引入了下面这种形式的写法,并且同时支持对传统 typedef
相同的功效:
通常我们使用
typedef
定义别名的语法是:typedef 原名称 新名称;
,但是对函数指针等别名的定义语法却不相同,这通常给直接阅读造成了一定程度的困难。
- typedef int (*process)(void *); // 定义了一个返回类型为 int,参数为 void* 的函数指针类型,名字叫做 process
- using process = int(*)(void *); // 同上, 更加直观
- template <typename T>
- using NewType = SuckType<int, T, 1>; // 合法
template<typename... TS> class Magic;
获取参数个数
- template<typename ...Args>
- void magic(Args ...args)
- {
- std::cout << sizeof...(args) << std::endl;
- }
对参数进行解包
- template<typename T>
- void printf(T value)
- {
- std::cout << value << std::endl;
- }
- template<typename T, typename ...Args>
- void printf(T value, Args... args)
- {
- std::cout << value << std::endl;
- printf(args...);
- }
列表初始化展开
- template<typename T, typename... Argsa>
- auto print(T value, Args... args)
- {
- std::cout<<value<<std::endl;
- return std::initializer_list<T>
- {([&]{std::cout<<args<<std::endl;}(), value)...};
- }
变长参数模板示例,求最大值
- #include<iostream>
- template<typename T>
- T max(T a, T b)
- {
- if(a>=b)
- {
- return a;
- }
- return b;
- }
- template<typename T,typename... Args>
- T max(T a, T b, Args... args)
- {
- if(a >= b)
- {
- return max(a, args...);
- }
- return max(b, args...);
- }
- int main()
- {
- std::cout<<max(1,5,3,2,11);
- }
模板增强可见https://blog.csdn.net/tennysonsky/article/details/77389891
- auto basicLambda = [] {std::cout << "Hello, world!" <<endl;};
- basicLambda();
捕获分为引用捕获[&]和值捕获[=],可以让编译器自行推导应用列表。
参数写在[](参数),在C++14中类型可以使用auto。
- auto process = [&](int a, int b){std::cout<<(a+b)*c<"\n";};
- process(a,b);
Lambda表达式又称为匿名函数,有时候也无需指定其名字而直接调用。
[&](){std::cout<<"Hello world!\n"}();
C++11 std::function是一种通用、多态的函数封装,它的实例可以对任何可以调用的目标实体进行存储,复制和调用操作,它也是一种类型安全的函数的容器。
- #include <functional>
- #include <iostream>
- int foo(int para) {
- return para;
- }
- int main() {
- // std::function 包装了一个返回值为 int, 参数为 int 的函数
- std::function<int(int)> func = foo;
- int important = 10;
- std::function<int(int)> func2 = [&](int value){
- return 1 + value + important;
- };
- std::cout << func(10) << std::endl;
- std::cout << func2(10) << std::endl;
- }
- #include<functional>
- int foo(int a, int b, int c)
- {
- }
- int main()
- {
- //std::placeholders::_1表示对第一个参数占位
- auto bindFoo = std::bind(foo, std::placeholders::_1, 1, 2);
- bindFoo(1);
- }
右值引用(及其支持的Move语意和完美转发)是C++11加入的重大语言特性之一。从实践角度讲,它能够完美解决C++
中长久以来为人诟病的临时对象效率问题。从语言本身讲,它健全了C++中的引用类型在左值右值方面的缺陷。
为了测试,在编译时设置编译选项
-fno-elide-constructors
- class A
- {
- public:
- A() { std::cout << "Constructor" << std::endl; }
- A(const A&) { std::cout << "Copy Constructor" << std::endl; }
- ~A() {}
- };
- static A getA()
- {
- A a;
- return a;
- }
- int main()
- {
- std::cout<<i<<" "<<j<<"\n";
- A a = getA();
- return 0;
- }
如图,在这种情况下,调用了一次构造函数,return时调用了一次拷贝构造函数生成了一个临时的a,将它return回的时候返回给了main函数中的a,随机这个临时的a也就消失了。这个阶段浪费了不少的性能。
A &&a = getA();
但如果将它改成了右值引用,就只会调用一次拷贝构造函数用于生成临时的变量,临时的变量生命周期和右值引用的生命周期相等。
还有有关右值引用与深拷贝和转发详细可以参见https://www.cnblogs.com/qicosmos/p/4283455.html
引用计数这种计数是为了防止内存泄露而产生的。基本想法是对于动态分配的对象,进行引用计数,每当增加一次对同一个对象的引用,那么引用对象的引用计数就会增加一次,每删除一次引用,引用计数就会减一,当一个对象的引用计数减为零时,就自动删除指向的堆内存。
引用计数不是垃圾回收,引用技术能够尽快收回不再被使用的对象,同时在回收的过程中也不会造成长时间的等待,更能够清晰明确的表明资源的生命周期。
- auto pointer = std::make_shared<int>(10);
- auto pointer2 = pointer;
- auto pointer3 = pointer;
- int *p = pointer.get(); //这样不会增加引用计数
- pointer2.reset();
- pointer3.reset();
- std::cout<<pointer.use_count()<<std::endl;
独占指针,类似于之前的auto_ptr,
- #include <iostream>
- #include <memory>
- class A;
- class B;
- class A {
- public:
- std::shared_ptr<B> pointer;
- ~A() {
- std::cout << "A 被销毁" << std::endl;
- }
- };
- class B {
- public:
- std::shared_ptr<A> pointer;
- ~B() {
- std::cout << "B 被销毁" << std::endl;
- }
- };
- int main() {
- std::shared_ptr<A> a = std::make_shared<A>();
- std::shared_ptr<B> b = std::make_shared<B>();
- a->pointer = b;
- b->pointer = a;
- return 0;
- }
联系客服