我是靠谱客的博主 超帅玫瑰,最近开发中收集的这篇文章主要介绍C++11知识积累,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1、在C++03标准中,__cplusplus的值被预定为199711L,在C++11标准中,__cplusplus的值被预定为201103L,程序判断是否支持C++11(有些编译器是部分支持):

#if __cplusplus < 201103L
    #error "should use C++11 implementation"
#endif

2、尽量用assert(n>0)进行断言,程序发布时加上宏定义值NDEBUG把断言禁掉,调试的时候去掉NDEBUG宏

3、lambda函数的语法定义:(参数列表和返回类型都是可选的部分,捕捉列表和函数体都可能为空。最简略的lambda函数为:[]{};)

[capture](parameters)mutable->return-type{statement}

capture:捕捉列表,有以下几种:

               [var]表示值传递方式捕捉变量var

               [=]表示值传递方式捕捉所有父作用域的变量(包括this)

               [&var]表示引用传递捕捉变量var

               [&]表示引用传递捕捉所有父作用域的变量(包括this)

               [this]表示值传递方式捕捉当前的this指针

               值传递与引用传递的区别:按值方式传递的捕捉列表,其传递的值在lambda函数定义的时候就已经决定了。按引用传递的捕捉列表变量,其传递的值等于lambda函数调用时的值,举例如下:

#include<iostream>
using namespace std;

int main()
{
    int j=12;
    auto by_val_lambda=[=]{return j+1;};
    auto by_ref_lambda=[&]{return j+1;};
    cout<<"by_val_lambda:"<<by_val_lambda()<<endl;
    cout<<"by_ref_lambda:"<<by_ref_lambda()<<endl;
    j++;
    cout<<"by_val_lambda:"<<by_val_lambda()<<endl;
    cout<<"by_ref_lambda:"<<by_ref_lambda()<<endl;
}
运行结果:
by_val_lambda:13
by_ref_lambda:13
by_val_lambda:13
by_ref_lambda:14

(parameters):参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同()一起省略

mutable:mutable修饰符。默认情况下,lambda函数总是一个const函数,mutable可以取消其长良性。在使用该修饰符时,参数列表不可省略(即使参数为空)

->return-type:返回类型。不需要返回值的时候可以连同符号->一起省略。在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。

{statement}:函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕捉的变量。

4、关于继承,关键字final(阻止继承)和override(虚函数描述符,描述虚函数的时候,必须重载基类的同名虚函数,否则编译出错)

class Object{
    virtual void fun()=0;
    virtual void output()=0;
};

class Base:public Object{
    void fun() final; // 声明为final
    void output()override;    //如果名字写错或者参数写错,编译出错,主要用来明确多态
};

class Derived:public Base{
    void fun(); // 无法通过编译
};

5、C++11支持using基类的构造函数,之前只能using基类的普通函数,如下:

class Base {
public:
	Base(int i) {cout<<"Base construct i:"<<i<<endl;}
	Base(int i,int j) { cout << "Base construct i:" << i<<" j:"<<j << endl; }
	virtual ~Base() {}
	void f(double i) { cout << "Base:" << i << endl; }
};

class Derived :public Base{
public:
	using Base::Base;
	using Base::f;
	void f(int i,int j) { cout << "Derived:" << i << endl; }
};

int main()
{
	Base b;
	b.f(4.5);
	Derived d1(5),d2(5,6);//这里自动调用基类的构造函数
	d1.f(4.5);//如果没有using base::f会产生编译错误
    d1.f(5, 6);
}

输出结果为:
Base construct i:5
Base construct i:5 j:6
Base:4.5
Derived:5

6、移动构造函数:移动构造函数接受一个所谓的“右值引用”的参数,右值可以理解为临时变量的引用,然后将临时变量的数据指针转移给本对象成员,随后将临时变量的数据指针置空,如下:

class HasPtrMem
{
public:
	HasPtrMem() :d(new int(0)) {
		cout << "Construct:" << ++n_cstr << endl;
	}
	HasPtrMem(const HasPtrMem& h) :d(new int(*h.d)) {
		cout << "Copy construct:" << ++n_cptr << endl;
	}
	HasPtrMem(HasPtrMem&& h) :d(h.d) {	//移动构造函数
		h.d = nullptr;					//将临时值的指针成员置空
		cout << "Move construct:" << ++n_mvtr << endl;
	}
	~HasPtrMem() {
		delete d;
		cout << "Destruct:" << ++n_dstr << endl;
	}

public:
	int*	d;
	static int n_cstr;
	static int n_dstr;
	static int n_cptr;
	static int n_mvtr;
};

int HasPtrMem::n_cstr = 0;
int HasPtrMem::n_dstr = 0;
int HasPtrMem::n_cptr = 0;
int HasPtrMem::n_mvtr = 0;

HasPtrMem GetTemp() {
	HasPtrMem h;
	cout << "Resource from " << __func__ << ":" << hex << h.d << endl;
	return h;
}

int main()
{
	HasPtrMem a = GetTemp();
	cout << "Resource from " << __func__ << ":" << hex << a.d << endl;
}

输出结果:
Construct:1    //函数GetTemp()中变量h构造函数
Resource from GetTemp:0x603010 //函数GetTemp()第二句
Move construct:1    //函数GetTemp()第三句返回时会产生临时对象,调用移动构造函数(如果没有移动构造函数,将会调用拷贝构造函数,开销比较大)
Destruct:1    //函数GetTemp()中h变量析构
Move construct:2 //临时变量给main()函数中的a赋值,调用移动构造函数,同上
Destruct:2    //临时变量析构
Resource from main:0x603010    //调用main()函数中第二句
Destruct:3    //main()函数中变量a析构

7、auto作为类型指示符,以下几种情况不能用:

void fun(auto x=1){}    //1、auto函数参数,无法通过编译

struct str{
    auto var=10;    //2、auto非静态成员变量,无法通过编译
};

int main(){
    char x[3];
    auto y=x;
    auto z[3]=x;    //3、auto数组,无法通过编译

    //4、auto模板参数(实例化时),无法通过编译
    vector<auto> v={1};
}

  (1)对于函数fun来说,auto不能是其形参类型。如果需要泛型的参数,需要用使用模板;

  (2)非静态成员变量的类型不能是auto类型;编译器阻止auto对结构体中的非静态成员进行推导,即使成员拥有初始值;

  (3)编译器禁止声明auto数组;

  (4)实例化模板不能使用auto作为模板参数;

虽然以上四种情况基本相似,人为的观察很容易能推导出auto所在位置应有的类型,但现在的C++11的标志还不支持这样的使用方式。

8、decltype的应用

int main(){
    vector<int> vec;
    typedef decltype(vec.begin()) vectype;
    for(vectype i=vec.begin();i<vec.end();i++){
        //dosomething
    }
    for(decltype(vec)::iterator i=vec.begin();i<vec.end();i++){
        //dosomething
    }
}

9、遍历容器:

int main(){
{
    int arr[5]={1,2,3,4,5};
    for(int& e:arr)    //需要修改项用引用,这里int可用auto代替
        e*=2;
    for(int e:arr)    //不需要修改项直接用变量,这里int可用auto代替
        cout<<e<<'t';
}

10、for_each与transform

///将容器a中的元素求log后放入a中
std::transform(a.begin(),a.end(),a.begin(),[](double d)->double{return log(d);});
std::for_each(a.begin(),a.end(),[](double& d){d = log(d);});

///如两个vector<double>V,W要进行某个运算,如计算U = sin(V) + W,结果存入U;
std::transform(v.begin(),v.end (),w.begin (),u.begin (),[&](double a ,double b)->double{
return sin(a)+b;
});

///使用std::transform可以比较方便的实现序列容器的四则运算
stl提供了plus,multiplies,divides,modulus,negate等函数对象,方便实现加减乘除等运算
std::transform(a.begin(),a.end(),b.begin(),std::back_inserter(c),std::plus<double>());

11、原子类型(多线程时不用加锁)

还可以使用atomic类模板,使用自定义类型:std::atomic<T> t;

#include <atomic>
#include <thread>

atomic<int> a;
atomic<int> b;

int Thread1(int) {
	int t = 1;
	a.store(t, memory_order_relaxed);
	b.store(2, memory_order_release);// 本原子操作前所有的写原子操作必须完成
	return 0;
}

int Thread2(int) {
	while (b.load(memory_order_acq_rel) != 2); // 本原子操作必须完成才能执行之后所有的读原子操作
	cout << a.load(memory_order_relaxed) << endl;
	return 0;
}

int main()
{
	thread t1(Thread1, 0);
	thread t2(Thread2, 0);
	t1.join();
	t2.join();
	return 0;
}

12、default与delete

class Base{
public:
    Base(int i){}
    Base()=default;    ///显式声明系统提供的缺省构造函数
    Base(const Base& rb)=delete;    ///显式删除系统提供的缺省拷贝构造函数
};

bool isLucky(int number);

int main()
{
    isLucky('a');    ///存在隐式转换
    isLucky(true);   ///存在隐式转换
    isLucky(3.5);    ///存在隐式转换
}

///增加删除不想要的重载函数
bool isLucky(char)=delete;
bool isLucky(bool)=delete;
bool isLucky(double)=delete;

int main()
{
    isLucky('a');    ///错误!调用删除函数
    isLucky(true);   ///错误!
    isLucky(3.5);    ///错误!
}

13、枚举类型增加作用域限制

int main()
{
    enum Color{
        black,
        white,
        red
    };
    auto white=false;    //编译提示变量重定义
}

int main()
{
    enum class Color:uint32_t{    //可以指定枚举值得类型,不指定时编译器会自动指定
        black,
        white,
        red
    };
    auto white=false;        ///编译OK
    Color c = white;         ///编译错误,提示当前作用域没有这个枚举元素
    Color c = Color::white;  ///编译OK
}

14、智能指针std::unique_ptr和std::shared_ptr区别

它们的共同之处是开发人员不在需要关心指针所指向的内存的释放问题,当这块内存不在被引用时,那么会被自动释放。

区别是,指向同一内存的某一类型的指针的数量的多少,std::unique_ptr只允许一个,而std::shared_ptr允许多个。

std::unique_ptr ///最多允许一个,当这个智能指针被销毁时,对应的内存会被自动释放。
                ///如果你试图去拷贝一个std::unique_ptr,那么编译器会报错,例如下面的代码就会报错
std::unique_ptr<Base> p(new Base);  // 没有问题
std::unique_ptr<Base> p1 = p;  // 这里会有编译错误,不能拷贝std::unique_ptr

///但是std::unique_ptr支持move语法,所以你可以这样

std::unique_ptr<Base> p(new Base); // 没有问题
std::unique_ptr<Base> p1 = std::move(p); // 没有问题

///对于std::shared_ptr,上面的拷贝的那个例子就没有任何问题,

std::shared_ptr<Base> p(new Base); // 没有问题
std::shared_ptr<Base> p1 = p; // 没有问题

 

最后

以上就是超帅玫瑰为你收集整理的C++11知识积累的全部内容,希望文章能够帮你解决C++11知识积累所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部