C++笔记记录

目录

<一>c++智能指针shared_ptr跟unique_ptr针对析构函数调用区别

<二>C++Lamda表达式

 <三>sort方法的排序

<四> c++11几个特性提升编码效率

<五>c++程序内存模型


<一>c++智能指针shared_ptr跟unique_ptr针对析构函数调用区别

结论:
    shared_ptr:
            无论基类的析构函数是否虚化,释放对象时基类跟子类的析构函数都会调用.
    unique_ptr:
            基类析构函数虚化,释放对象时基类跟子类的析构函数都会调用.
            基类析构函数未虚化,释放对象时只会调用基类的析构函数,子类析构函数不会被调用.

代码:

代码:
#include <iostream>
#include <string>
#include  <memory> // 共享指针必须要包含的头文件
using namespace std;

class A {
  public:
    A(){
      cout<<"A=="<<endl;
    }
    virtual void fun() = 0;
    virtual ~A(){   // 是否虚化决定unique_ptr指针在调用析构函数是否调用子类析构函数
      cout<<"~A--"<<endl;
    }
};

class B:public A{
  public:
    B(){
      cout<<"B=="<<endl;
    }
    void fun(){
      cout<<"B - fun()"<<endl;
    }
    ~B(){
      cout<<"~B--"<<endl;
    }
};


int main(){
  // std::shared_ptr<A> sa(new B());
  // sa->fun();

  std::unique_ptr<A> ua(new B());
  ua->fun();
  return 0;
}

结果:
// 基类析构函数未虚化
shared_ptr:
    [root@local mydir]# g++ -g zhineng_ptr.cpp -std=c++11
    [root@local mydir]# ./a.out 
    A==
    B==
    B - fun()
    ~B--      [调用了子类的析构函数]
    ~A--


unique_ptr: 
    [root@local mydir]# 
    [root@local mydir]# 
    [root@local mydir]# g++ -g zhineng_ptr.cpp -std=c++11
    [root@local mydir]# ./a.out 
    A==
    B==
    B - fun()
    ~A--   [仅仅调用基类的析构函数]


//基类析构函数虚化
shared_ptr:
    [root@local mydir]# g++ -g zhineng_ptr.cpp -std=c++11
    [root@local mydir]# ./a.out 
    A==
    B==
    B - fun()
    ~B--
    ~A--

unique_ptr: 
    [root@local mydir]# 
    [root@local mydir]# g++ -g zhineng_ptr.cpp -std=c++11
    [root@local mydir]# ./a.out 
    A==
    B==
    B - fun()
    ~B--
    ~A--

原因:

<二>C++Lamda表达式

a.表达式语法结构:       

     lamda表达式函数会自动推到返回值无需编写,带返回值的写法

    // lamda表达式函数会自动推到返回值无需编写,带返回值的写法
    auto pFunRet = []() -> std::string {
        std::string s("rrrrr");
        return s;
    }; 
    std::cout<<"pFunRet() = "<<pFunRet().c_str()<<std::endl;

b.无参表达式

 // 无参Lamda函数表达式
    auto pFun = [](){
        std::cout<<"This is lamda fun"<<std::endl;
    };
    pFun();

c.有参表达式

   // 带参数
    auto pFunParams = [](int a,std::string s){
         std::cout<<"Params lamda="<<a<<"-"<<s.c_str()<<std::endl;
    };
    pFunParams(100,"LX");

d.值捕获(&附加说明)

  值捕获,捕获作用域所有值,=可以替换成某个具体的值,表示单个变量捕获

  // 值捕获,捕获作用域所有值,=可以替换成某个具体的值,表示单个变量捕获
    int val_0 = 1;
    int val_2 = 2;
    auto pFunValue = [=](){
        // 值捕获都是只读权限,强行改变会编译报错,提醒read-only 'val_0'
        // val_0 += 1;
        // val_2 += 2;
        std::cout<<"val_0="<<val_0<<" val_2="<<val_2<<std::endl;
    };
    pFunValue();

 mutable 修饰等价于函数内部做了一份拷贝,之后再对拷贝进行操作,故而可以修改其捕获value2, 

   // 值捕获2
    int value2 = 100;
    std::cout<<"before modify value2="<<value2<<std::endl;
    auto pFunValue2 = [value2]() mutable { 
        // mutable 修饰等价于函数内部做了一份拷贝,之后再对拷贝进行操作,故而可以修改其捕获value2,如下输出结果展示:
        // before modify value2=100
        // value2=211
        // after modify value2=100
        value2 += 111;
        std::cout<<"value2="<<value2<<std::endl;
    };
    pFunValue2();
    std::cout<<"after modify value2="<<value2<<std::endl;

e.引用捕获

 引用修改的是捕获的变量的本身,如下输出结果:

  // 引用捕获
    int quote = 100;
    std::cout<<"before modify quote="<<quote<<std::endl;
    auto pFunQuote = [&quote](){
        quote += 20; // 引用修改的是捕获的变量的本身,如下输出结果:
        //before modify quote=100
        //quote=120
        //after modify quote=120
        std::cout<<"quote="<<quote<<std::endl;
    };
    pFunQuote();
    std::cout<<"after modify quote="<<quote<<std::endl;
    return 0;

 <三>sort方法的排序

sort(v.begin(),v.end()) // 默认生序排序

sort(v.begin(),v.end(),cmp)// 排序根据自定义的cmp排序方式。

c++11中使用对应的模版:

greater<int>【生序】,less<int>两种模版进行排序【降序】

sort(v.begin(),b.end(),comp()) //comp后面必须跟括号,因为这里使用的是函数对象,仿函数

struct comp{

    bool operator(int & a,int b){

        return a < b;
    }

};

2.利用数据流进行类型之间转换,一般使用整形跟字符串使用比较多,另外系统库自带to_string转字符串。

template<typename out_type, typename in_type>
out_type convert(const in_type & in_obj) 
{   
	std::stringstream ss;
	ss << in_obj;
	out_type out_obj;
	ss >> out_obj;
	return out_obj;
} 

3.c++中宏定义重的#跟##的用法

#:字符串拼接
// 预编译时,将传递的参数拼接成字符串的形式
#define CONVERT(name,DDD) #name#DDD
 
// int main(int argc, char* argv[])
// {
//     printf("You and %s are friends.\n", CONVERT(ll,dddd));
//     return 0;
// }
输出:lldddd
------------------------------------------------------------
##: 用于拼接代码块
// ## 主要用于拼接代码如下;
class Obj{

  public:
    Obj(){}
    ~Obj(){}
    void fun(){std::cout<<"Obj++"<<std::endl;}
};

#define NEW_OBJ(obj)                                  \
    std::shared_ptr<Obj> _ptr_##obj##__(new Obj());    \
    auto &obj = *_ptr_##obj##__;

int main()
{
  NEW_OBJ(test_obj);
  test_obj.fun();
  return 0;
}
输出:
    Obj++

注:以上方式,在公共方式合适的地方使用会有好的效果,避免过多滥用,不然导致代码为户型非常差。

4.利用io数据流进行类型拆分转换

a.拆分字符串并转化成整形List,相互转化

#include <iostream>
#include <istream>
#include <sstream>
#include <vector>
#include <string>

using namespace std;

void Str2UintList(const string &str,vector<int> &vec) {
    if(str.empty()){
        return;
    }
    istringstream ss(str);
    int tmp = 0;
    char delim = 0;
    while(ss >> tmp){
        vec.push_back(tmp);
        ss >> delim;

    }
}


void UintList2Str(string &str,const vector<int> &vec){
    if(vec.empty()){
        return;
    }

    ostringstream os;
    for(auto & v : vec){
        os << v <<';';
    }
    str = os.str();
}
int main()
{
    string str = "111,222,3333";
    vector<int> vec;
    Str2UintList(str,vec);
    for(int i = 0;i < vec.size();++i)
    {
        cout<<vec[i]<<endl;
    }
    string os_str;
    UintList2Str(os_str,vec);
    cout<<os_str.c_str()<<endl;
    return 0;
}

<四> c++11几个特性提升编码效率

(1),=delete :用来修饰一些函数默认不自己生成(比如拷贝构造,赋值函数)

// 初始化&delete用法:
class A{
  public:
    A():arr{0} // 
    {}  

    //注解标签
    // [[deprecated("use funcY instead")]] void funcX()
    // {
    //   //实现省略
    // }
    A(const A& a) = delete;
    A& operator =(const A& a) = delete;

  private:
    int arr[10];
    std::string s{"lx"};
    bool flag{true};
};

(2) =default :
        如果一个C++类没有显式给出构造函数、析构函数、拷贝构造函数、 operator= 这几类函数的实现,则在需要它们时,编译器会自动生成;或者,在给出这些函数的声明时,如果没有给出其实现,则编译器在链接时会报错。如果使用=default标记这类函数,则编译器会给出默认的实现。
(3)override:
        用于编译检测继承类函数重载(避免因子类重写跟父类函数有差异从而导致不能实现多态机制)
    多线程实现:
      std::thread、std::mutex、std::condition_variable 少数几个线程同步对象即可,进行多线程编程. 

(4) c++11左右值

 左值:通常=左边的值成为左值,且可以取地址,有名称,如下:

  int a; //  左值
  int & b = a; // 左值引用左值
  const int & c = a; // 左值引用

左值得引用 就是一般引用,一根使用&变量名表示:

int a = 111;
int &b = a; // 取得a的引用

 右值:赋值运算符=右边的值成为右值.


    int t = fun();// fun() 为右值
    int a;
    int b;
    int c = a + b; // a+b右值
    char *p ="abc"; // "abc" 为右值

右值引用使用&&表示右值引用:

int && a = fun(); // a就是右值引用,fun()返回就是一个右值

**{ 右值引用也相当于别名,与左值的区别为右值引用是无名变量的别名。
fun() 是一个返回右值的函数,右值在这一句执行完就该结束他的生存期了,如果是对象就该调用析构函数 了;但是使用右值引用,从而右值引用指向右值,右值的生命周期右值引用通等了, 相比直接赋值少一次对象的析构和构造}.

右值引用减少对象的拷贝,从而提升系统的性能.

右值引用主要有两个用处,一个是移动语义,一个是完美转发。
 a>移动语义

   C++11中引进的一种move语义,作用是在一些对象构造是已经分配的资源,再其他对象使用时,不需要通过拷贝,重新申请内存,通过移动而非拷贝将对应的资源进行接管使用。

移动构造函数,使用的是&&表示:

   // 移动构造
   A(A && a):p(a.p){
       cout<<"A(A && a) = "<<"move"<<endl;
   }

例如移动拷贝使用:

#include <iostream>
using namespace std;
class A {
    public:
        A():p(new int[5]){
            cout<<"A() = "<<"default - construct = "<<endl;
        }

        A(A & a):p(new int[*a.p]){
            cout<<"A(A & a) = "<<"copy - construct"<<endl;
        }
    
        // 移动构造
        A(A && a):p(a.p){
            cout<<"A(A && a) = "<<"move"<<endl;
        }
        ~A(){
            cout<<"~A()= "<<"destory"<<endl;
            delete p;
        }
        int * p;
};

A get(){
    return A();
}
int main(){
    
    cout<<"1............"<<endl;
    A a;

    cout<<"2............"<<endl;
    A b(a);  //copy

    cout<<"3............"<<endl;
    A c = a;  //copy

    cout<<"4............"<<endl;
    A d = get();  //两个移动构造调用, 
    // 右值引用,返回一个局部变量对象,此时通过移动构造进行构造d
    // getM函数返回一个对象,此时临时对象是右值,调用一次移动构造,根据开启临时对象
    // -fno-elide-constructors 该参数关闭编译器省略复制构造优化
    // getM()返回前,A()先移动构造一个临时对象,再者通过临时对象再调用移动构造初始化d
    /*
        [root@localhost c++]# ./a.out 
        1............
        A() = default - construct = 
        2............
        A(A & a) = copy - construct
        3............
        A(A & a) = copy - construct
        4............
        A() = default - construct = 
        A(A && a) = move
        ~A()= destory
        A(A && a) = move
        ~A()= destory
        5............
        A(A && a) = move
        ~A()= destory
        ~A()= destory
        ~A()= destory
        ~A()= destory
        ~A()= destory
        [root@localhost c++]#
    */
    cout<<"5............"<<endl;
    A f(move(a)); //move
    return 0;
}

  std::move函数:

int a =1;
int & b = a; // 左值引用
int && c = a;// a为一个左值,不用于到右值引用,编译会报错
int && d = std::move(a); // 将左值a变成右值

  通过move将左值转化成右值:

int main()
{
    std::string s = "Hello"; //Hello一个右值
    std::cout << "before s is " << s.c_str() <<endl;
    std::string t = std::move(s); // 调用move将s转化成右值
    std::cout << "After move, s is " << s <<endl;
    std::cout << "Current t is "<<t.c_str()<<endl;
}

输出:
[root@localhost c++]# ./a.out 
before s is Hello
After move, s is      // s资源转移,已经没法使用了
Current t is Hello
[root@localhost c++]# 

  b>完美转发:

   c++11通过std::forward()函数来实现,对应的完美转发;

   如下:

#include <iostream>
#include <utility>
#include <vector>
#include <string>
using namespace std
template<class T> 
void fun(T & c){ // 左值引用
    cout<<"t = "<<"left  - reference"<<endl;
}
template<class T>
void fun(T&& c){// 右值引用
    cout<<"t = "<<"right -  reference"<<endl;
}

template<class   T>
void Uc(T&& c){ //万能引用格式
    fun(std::forward<T>(c)); // 保持原样类型进行传递,被称为完美转发
}

int main()
{
    int n = 111;
    Uc(n);   // 左值
    Uc(111); // 右值
    return 0;
}

<五>c++程序内存模型

   如下图:

上图三个申请内存函数的含义:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值