C++11新特性

1.C++11中引入了哪些新的智能指针类型?

为什么要引用智能指针?

首先裸指针使用完没有及时释放会造成内存泄漏的问题,为了解决这个问题,引入了独占型智能指针unique_ptr,它不需要手动释放。但是unique指针不允许其他智能指针和它指向同一份资源。
共享型指针指针shared_ptr出现,shared_ptr允许智能指针和它指向同一份资源,但是又产生了新的问题,就是会造成循环引用的问题,导致资源无法被释放。
监控型智能指针weak_ptr出现,,它的特点是引用对象时引用计数不增加,但是智能成为资源的监督者,无法使用*和->操作资源。

unique_ptr(独占型智能指针)

unique指针不允许其他智能指针和它指向同一个资源

实现的方法就是在独占型指针指针的类里面把拷贝构造函数delete了
#include <iostream>
#include <memory>
using namespace std;


int main(){
    unique_ptr<int> uPtr1(new int(1));
    unique_ptr<int> uPtr2(uPtr1);//×
    return 0;
}

可以使用move函数把一个unique指针的资源转移给另外一个unique指针

#include <iostream>
#include <memory>
using namespace std;


int main(){
    unique_ptr<int> uPtr1(new int(1));
    unique_ptr<int> uPtr2(move(uPtr1));
    return 0;
}

不要用原始指针去初始化多个指针智能,会造成一份资源被多次释放

产生的原因:因为用原始指针去初始化智能指针,引用计数不会++
#include <iostream>
#include <memory>
using namespace std;


int main(){
    int* oldPtr = new int(1);
    unique_ptr<int> uPtr1(oldPtr);
    unique_ptr<int> uPtr2(oldPtr);
    return 0;
}

shared_ptr(共享型智能指针)

允许多个智能指针指向同一份资源

#include <iostream>
#include <memory>
using namespace std;


int main(){
    shared_ptr<int> shrPtr1(new int(10));
    shared_ptr<int> shrPtr2(shrPtr1);
    shared_ptr<int> shrPtr3(shrPtr1);
    shared_ptr<int> shrPtr4(shrPtr1);
    shared_ptr<int> shrPtr5(shrPtr1);
    cout<<shrPtr5.use_count()<<endl;
    return 0;
}

会造成循环引用的问题,导致资源无法被释放

图例

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码

#include <iostream>
#include <memory>

using namespace std;
class B;
class A{
public:
    int* a;
    shared_ptr<B> ptrB;
    A(int val){
        a = new int(val);
    }
    void setShrPtr(shared_ptr<B> ptr){
        this->ptrB = ptr;
    }
    ~A(){
        cout<<"A::析构函数"<<endl;
    }
};
class B{

public:
    int* b;
    shared_ptr<A> ptrA;
    B(int val){
        b = new int(val);
    }
    void setShrPtr(shared_ptr<A> ptr){
        this->ptrA = ptr;
    }
    ~B(){
        cout<<"B::析构函数"<<endl;
    }
};


int main(){
    shared_ptr<A> shrPtrA(new A(1));
    shared_ptr<B> shrPtrB(new B(2));
    shrPtrA->setShrPtr(shrPtrB);
    shrPtrB->setShrPtr(shrPtrA);
    cout<<"shrPtrA.use_count "<<shrPtrA.use_count()<<endl;
    cout<<"shrPtrB.use_count "<<shrPtrB.use_count()<<endl;
    //没有调用析构函数!!!!!!!!!!!!!!
//    unique_ptr<int> unqPtr1(new int(100));
//    unique_ptr<int> unqPtr2(std::move(unqPtr1));
//    cout<<*unqPtr2<<endl;
    return 0;
}

weak_ptr(监督型智能指针)

为了解决shared指针循环引用的问题,所以要和shared指针一起使用,用来监督shared_ptr的使用情况

代码

#include <iostream>
#include <memory>

using namespace std;
class B;
class A{
public:
    int* a;
    weak_ptr<B> ptrB;
    A(int val){
        a = new int(val);
    }
    void setShrPtr(shared_ptr<B> ptr){
        this->ptrB = ptr;
    }
    ~A(){
        cout<<"A::析构函数"<<endl;
    }
};
class B{

public:
    int* b;
    weak_ptr<A> ptrA;
    B(int val){
        b = new int(val);
    }
    void setShrPtr(shared_ptr<A> ptr){
        this->ptrA = ptr;
    }
    ~B(){
        cout<<"B::析构函数"<<endl;
    }
};


int main(){
    shared_ptr<A> shrPtrA(new A(1));
    shared_ptr<B> shrPtrB(new B(2));
    shrPtrA->setShrPtr(shrPtrB);
    shrPtrB->setShrPtr(shrPtrA);
    shrPtrA->ptrB.lock()->b;
    shrPtrB->ptrA.lock()->a;
    cout<<"shrPtrA.use_count "<<shrPtrA.use_count()<<endl;
    cout<<"shrPtrB.use_count "<<shrPtrB.use_count()<<endl;
//    unique_ptr<int> unqPtr1(new int(100));
//    unique_ptr<int> unqPtr2(std::move(unqPtr1));
//    cout<<*unqPtr2<<endl;
    return 0;
}

自定以MyShared_ptr实现

-------------------------------------MyShared_ptr.h--------------------------------------------------

#ifndef TEST02_MYSHARED_PTR_H
#define TEST02_MYSHARED_PTR_H
#include <iostream>

using namespace std;

template <typename T>
class Ref{
    T* obj = nullptr;
    int r_count = 0;
public:
    Ref(T* oldPtr):obj(oldPtr){
        increase();
    }
    inline void increase(){
        r_count++;
    }
    inline void reduce(){
        r_count--;
        if(r_count == 0){
            delete obj;
            delete this;
        }
    }
    T* getObj(){
        return obj;
    }
    int getUseCount(){
        return r_count;
    }
};
//ptr1 ptr2 ptr3 ptr4 -->obj
template <typename T>
class MyShared_ptr {
    Ref<T>* r = nullptr;
public:
    MyShared_ptr() = default;
    ~MyShared_ptr(){
        if(r) r->reduce();
    }
    MyShared_ptr(T* oldPtr){
        cout<<"调用构造函数"<<endl;
        r = new Ref<T>(oldPtr);
    }
    MyShared_ptr(const MyShared_ptr& other){
        cout<<"拷贝构造"<<endl;
        if(other.r){
            this->r = other.r;
            r->increase();
        }
    }
    MyShared_ptr(MyShared_ptr&& other){
        cout<<"移动构造"<<endl;
        if(other.r){
            this->r = other.r;
            other.r = nullptr;
        }
    }
    MyShared_ptr operator=(const MyShared_ptr& other){
        if(other.r && this->r){
            this->r->reduce();
            this->r = other.r;
            r->increase();
            return *this;
        }
    }
    MyShared_ptr operator=(MyShared_ptr&& other){
        if(other.r && this->r){
            this->r->reduce();
            this->r = other.r;
            return *this;
        }
    }
    void reset(){
        if(r) r->reduce();
        r = nullptr;
    }
    void reset(T* trg){
        if(r) r->reduce();
        r = new Ref<T>(trg);
    }
    T operator*(){
        if(r){
            return *r->getObj();
        }
    }
    T* operator->(){
        if(r){
            return r->getObj();
        }
    }
    int use_count(){
        if(r) return r->getUseCount();
        return 0;
    }
};


#endif //TEST02_MYSHARED_PTR_H

2.解释一下C++11中的右值引用和移动语义

右值引用接收右值

右值指定就是没有函数名不能寻址的值

移动语义

指的是将一个资源从一个对象1移动给另外一个对象2,对象1自动放弃了对该资源的持有,给到了资源2,不用进行资源的拷贝,可以提高性能。
通常通过移动构造和移动赋值运算符来实现
如果一个值为左值想使用移动语义,可以调用std::move函数把左值移动为右值
#include <iostream>

using namespace std;
class MyObject{
    int* data;
public:
    MyObject(): data(nullptr){
        cout<<"Default Constructor"<<endl;
    }
    MyObject(int value): data(new int(value)){
        cout<<"Regular Constructor"<<endl;
    }
    MyObject(MyObject&& other):data(other.data){
        other.data = nullptr;
        cout<<"Move constructor"<<endl;
    }
    ~MyObject(){
        delete data;
        cout<<"Destructor"<<endl;
    }
    void printData() const {
        if(data != nullptr) cout<<"Data: "<<*data<<endl;
        else                cout<<"Data is null"<<endl;
    }
};

int main(){
    MyObject obj1(10);
    obj1.printData();
    MyObject obj2(std::move(obj1));
    obj2.printData();
    obj1.printData();
    return 0;
}

3.谈谈你对C++11中auto关键字的理解

为什么要有auto这个关键字

为了简化代码,auto可以自动推断出表达式的类型

auto

auto是C++11引入的一种自动推断类型的机制

auto的使用场景

使用STL容器的时候,它的迭代器类型可能会非常复杂,这时候使用auto关键字可以提高可读性

使用增强for循环的时候,不用指定下标他会自动从头到尾遍历

自动推断函数的返回值

auto add(int a,int b){
    return a+b;
}
int main(){
    vector<int> vec = {1,2,3,4};
    auto iter = vec.begin();
    for(auto it : vec){
        cout<< it <<endl;
    }
    return 0;
}

使用泛型编程,当参数类型非常复杂且难以指定

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

template <typename T>
void process(const vector<T>& vec){
    for(auto it : vec){
        cout << it<<endl;
    }
}
int main(){
    vector<int> vec = {1,2,3,4};
    process(vec);
    return 0;
}

4.C++11的lambda表达式是什么?

为什么要有lambda表达式?

首先它可以简化代码,因为它可以不想传递的函数一样,需要通过函数调用来执行,而是直接在调用处把函数体放在了那里

lambda表达式

本质是一个匿名函数对象

格式

[捕获列表] (参数列表) -> ret {
    //函数体
}
//捕获列表有值捕获和引用捕获 this在类中捕获成员变量
//返回值通常可以省略,可以自动判断

应用场景

直接使用

作为函数的参数

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;



int main(){
    auto add = [](int a, int b){
        return a+b;
    };
    cout<<add(1,2)<<endl;
    vector<int> vec = {5,3,1,4,2};
    sort(vec.begin(),vec.end(),[](int l,int r){
        return l<r;
    });
    for(auto it : vec){
        cout<<it<<' ';
    }
    return 0;
}

5.C++11中的nullptr和C++98中NULL的区别

C++98中的NULL在函数重载的时候可能会造成指代不明确的问题,因为NULL是一个宏定义的0

而C++11中引入的nullptr则是真正的空指针,它的类型不是宏定义的0,而是nullptr_t可以自动转换成任意指针类型

/*
 * C+11 nullptr 和 C++98 NULL 的区别
 */
void overloadFunc(int* ptr){
    printf("void overloadFunc(int* ptr)\n");
}
void overloadFunc(int ptr){
    printf("void overloadFunc(int ptr)\n");
}

6.C++11引入了哪些新的特性或改进?

a.引入了三个智能指针

独占型智能指针(unique_ptr)

共享型智能指针(shared_ptr)

为了解决shared_ptr循环问题的weak_ptr智能指针(weak_ptr)

b.提供了lambda表达式来定义匿名函数对象

c.引入了auto自动检测表达式类型的关键字

d.引入了移动语义

移动构造
移动赋值运算符
和move函数把左值改变成右值

e.为了解决98中NULL指代不明确的问题,C++11引入了nullptr

f.提供了thread、mutex、条件变量等有关线程的概念

g.提供了STL库,定义了一些常用的数据结构

例如stack、queue、priotity_queue、list、unordered_map、map等等常用的数据结构

h.引入了随机数函数库random

(*)i.提供了正则表达式库

7.解释一下C++中的增强for循环及其用法

为什么要引入增强for循环

感觉发现了一个规律,C++11引入的这些新特性大多数都是为了使代码变得更加简洁,增强for循环也不例外

增强for循环

格式

for(定义了一个变量 : 遍历的范围)

通常和auto自动检测类型的关键字一起使用

尤其是对于STL库中的容器而言,用增强for循环加auto配合使用会时代码变得更加简洁

应用场景

不写了 上面auto使用的时候有

8.C++11中如何初始化一个数组或容器

C++11中可以使用列表进行初始化

like

vector<int> vec = {1,2,3,4}

9.谈一谈C++11中的默认和删除函数

默认函数(default)

为什么要使用默认函数?

在一个类中,如果我们定义了一个有参构造了,系统就不会提供无参构造了,也就时说我们在main中使用MyClass myClass;这样时错的
为了解决这个问题,C++11为我们提供了默认函数。你也可以自己定义一个默认的构造,但是C++11的这种 = default更加简洁
#include <iostream>

using namespace std;

class MyClass{

public:
    string name;
    int age;
    MyClass() = default;
    MyClass(int a){
        cout<<"有参构造"<<endl;
    }
};
int main(){
    MyClass obj;
    cout<<"name "<<obj.name<<endl;
    cout<<"age "<<obj.age<<endl;
    return 0;
}

删除函数(delete)

在我的Qt网盘项目中客户端和服务器需要实现单例模式,即需要把构造函数私有化,禁止使用构造函数赋值运算符去创建对象,想要创建对象必须通过一个static全局访问结点来创建对象,由此来实现单例模式
而把拷贝构造和赋值运算符禁止使用的就时删除函数 =delete
#include <iostream>

using namespace std;

class SingleClass{
public:
    static SingleClass getInstance();
private:
    int* resource;
    SingleClass();
    SingleClass(const SingleClass& other) = delete;
    SingleClass& operator=(const SingleClass& other) = delete;
    
};
int main(){
    SingleClass::getInstance();
    return 0;
}

10.谈一谈initalizer_list显示转换运算符

在C++11中,initializer_list是一个模板类,可以接收花括号这种形式的初始化参数列表作为参数,从而简化对象和容器的初始化

#include <iostream>

using namespace std;

class MyClass{
public:
    MyClass(initializer_list<int> list){
        for(auto it : list){
            cout<<it<<' ';
        }
    }
};
int main(){
    MyClass obj = {1,2,3,4};
    return 0;
}

11.原子操作及其在多线程编程中的应用

为什么要引入原子操作?

为了解决多线程并发执行产生的安全性问题,C++11引入了原子操作的概念,通过 头文件提供了对原子类型的支持。

原子操作

原子操作是指再多线程环境中,对变量的操作可以在单个指令中完成,不会被其他线程打断,从而实现线程并发安全执行

12.谈一谈为什么move函数可以优化资源转化性能

首先move函数可以将对象转化为右值引用类型,从而可以使用移动构造、移动赋值运算符等移动语义来提高资源转化的效率
具体提高的方式是,移动语义下的资源转换不是想拷贝构造哪种,拷贝一份资源再去赋值,而是直接把你的东西给我,省略了拷贝的过程,从而提高了效率
  • 28
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值