C++11特性总结

系列文章目录


一、类型

1.auto用法

注意要点:编译器在编译阶段完成对auto的推导,就必须能让编译器推出其类型。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
using namespace std;

double foo()
{
	return 1.1;
}

struct Test
{
	int a;
};

void func(vector<int> &tmp)
{
	for (auto i = tmp.begin(); i != tmp.end(); ++i)
	{
		//……
	}
}

int main(void)
{
	auto b = 1;  //b的类型就是int
	auto c = foo(); //c的类型就是double
	Test str = { 0 };
	auto d = str; //d的类型就是struct Test
	return 0;
}


________________________________________________________________________________________________________
auto易错点
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
using namespace std;

//2、vs2013不支持,函数形参是auto变量, qt确实可以
void func(auto a)
{
}

//3、auto变量不能作为自定义类型的成员变量
struct  Test
{
	int a;
	auto b = 10;
};

int main(void)
{
	//1、定义变量时,必须初始化
	auto a;
	a = 10;

	auto b[3] = { 1, 2, 3 }; //4、不能是auto数组

	//5、模板实例化类型不能是auto类型
	vector<int> a;
	vector<auto> b = { 1 };

	system("pause");
	return 0;
}

2.数据类型

定义数据类型
typedef int int32;
using my_int=int;   //C++11方式
cout << is_same<int32, m_int>::value << endl;

decltype自动推导类型
	float a;
	double b;
	decltype(a + b) c;
	cout << typeid(c).name() << endl;
	
auto作为返回值和追踪返回类型
auto func2(int a, int b) 
{
	return a + b;
}

auto func3(int a, double b) -> decltype(a+b)
{
	return a + b;
}

类中成员变量初始化
class A
{
public:
	A(int i) : a(i) //参数列表初始化
	{
		//a =  i;
	}

	int a;
};
class B
{
public:
	int data{ 1 };
	int data2 = 1;
	A tmp{ 10 }//新的类初始化方法
	string name{ "mike" };//新的类初始化方法
};

初始化列表
	int a = 1;
	char b = { 55}; //ok, 列表初始化
	int c{ 2 }; //ok
	cout<<b<<endl;
	int arr[] = { 1, 2, 3 };
	int arr2[]{ 1, 2, 3 };
{}类型转化可以检测是否精度丢失
	int a = 1024,c=1char b = a; //ok, 数据丢失, 类型收窄
	char b = { a }; //编译阶段错误,精度丢失
	char d=c;//ok,数据不丢失
	char d={c};//即使数据不丢失,编译也会报错,因为精度发生了丢失。


基于范围的for循环
	for (int &tmp : a)//tmp作为别名,可以改变数组里的值
	{
		tmp = 2 * tmp;
		cout << tmp << ", ";
	}
	for (int tmp : a)//值传递,tmp不能改变数组里的值
	{
		cout << tmp << ", ";
	}

不能循环传参进来的数组里的值,因为传进来的是数组的地址。
void func(int a[]) //形参中的数组不是数组,是指针变量,无法确定元素个数
{
	//基于范围的for, 这个范围要确定后,才能使用
	for (int & tmp: a) //err,
	{
		cout << tmp << endl;
	}
}
静态断言
static_assert(sizeof(void *) == 4, "64位系统不支持");

枚举类型:枚举元素的作用域是全局的,所以其他枚举就不能使用其枚举元素名了
int main(void)
{	//Ok、Error都是int类型,而且status中的枚举元素不能再次作为其他枚举的元素了
	enum Status {Ok, Error=4};//Error是int类型
	// “Ok”: 重定义;以前的定义是“枚举数”	
	//enum Status2 { Ok, Error };
	Status flag = Ok;
	cout << sizeof(Ok)<<flag << endl; //4
	return 0;
}

强枚举类型:枚举元素的作用域只在强枚举类型中,所以其他强枚举可以使用相同的枚举名。
int main()
{
	//强类型枚举, enum后面加上class或struct修饰
	enum class Status { Ok, Error };
	enum struct Status2 { Ok, Error };

	//Status flag = Ok; //err, 必须枚举类型的作用域
	Status flag = Status::Ok; //ok

	//强类型枚举,可以指定成员变量的类型
	enum struct Status3: char { Ok, Error };//指定枚举元素的类型
	cout << sizeof(Status3::Ok) << endl;

	enum struct Status4: long long { Ok, Error };
	cout << sizeof(Status4::Ok) << endl;

	system("pause");
	return 0;
}

常量表达式constexpr:修饰函数,变量,在编译阶段就得到结果,修饰函数时,函数里只能有typedef, using指令,静态断言和return;也就是说,被constexpr修饰的变量和函数里不能存在不能在编译阶段得到结果的变量。
constexpr int GetNum3()
{//常量表达式,发生在编译阶段
  //允许包含typedef, using指令,静态断言
    static_assert(1, "fail");
    return 3;
}

int main()
{
    //error: enumerator value for 'e1' is not an integer constant
    //枚举成员初始化,必须是整型常量

    enum {e1=GetNum3(), e2}; //ok
    constexpr int tmp = GetNum3(); //ok, 发生在编译阶段
    enum {a1 = tmp, a2};//ok

    return 0;
}

原生字符串
cout << R"(hello, \n world)" << endl;//翻译字符不会翻译

3.类

继承构造,在子类中using A::A,使用父类的构造函数,子类中不能在写自己的构造函数。
//基类
class A
{
public:
    A(int x, int y)
    {
        a = x;
        b = y;
    }

protected:
    int a;
    int b;
};

//派生类
class B:public A
{
public:

    //继承构造
    using A::A;

    void display()
    {
        cout << "a = " << a << ", b = " << b << endl;
    }

    //没有增加新的成员变量
    int tmp;
};

int main()
{
    //派生类对象
    B obj(10, 20);
    obj.display();



    return 0;
}

委托构造
class Test
{
public:
    //委托构造,一定要通过初始化列表方式
    Test():Test(1, 'a')
    {

    }

    Test(int x): Test(x, 'b')
    {

    }
    Test(char x): Test(11, x)
    {

    }

    int a;
    char b;

private:
    Test(int x, char y): a(x), b(y)
    {

    }



};

final和override
 virtual void func() final {} //这是最终版本的虚函数,不能再重写
 //在重写虚函数地方,加上override, 要求重写的虚函数和基类签名(函数名和形参数和返回值)一模一样,其作用就是怕程序员把函数名和形参数和返回值给写错了。
   virtual int func(int b) override{ }

C++ 的类有四类特殊成员函数,它们分别是:默认构造函数、析构函数、拷贝构造函数以 及拷贝赋值运算符。
C++11 标准引入了一个新特性:"=default"函数。程序员只需在函数声明后加上“=default;”, 就可将该函数声
明为 "=default"函数,编译器将为显式声明的 "=default"函数自动生成函数 体
class X
{
public:
    X() =default; //让编译器提供一个默认的构造函数,效率比用户写的高

    X(int i)
    {//写了带参的构造函数,编译器不会提供无参的构造函数
        a = i;
    }

    int a;
};
delete禁止函数使用 全局函数,类成员函数均可使用。
class X
{
public:
    X() {} //普通无参
    X(const X &)=delete; //拷贝构造, 用"=delete"修饰,此函数被禁用
    X & operator=(const X &)=delete; //赋值运算符重载函数, 用"=delete"修饰,此函数被禁用

    X(int) =delete; //用"=delete"修饰,此函数被禁用

    void *operator new(size_t) =delete;
    void *operator new[](size_t) =delete;
};
模板的默认参数,类模板默认参数必须从由向左写。
//1、类模板支持默认的模板参数
template<class T, class T2=int> //类模板的模板参数必须是从右往左
class A{};
//2、C++11才支持,函数模板带默认的模板参数
//  函数模板的模板参数可以是从右往左,也可以是从左往右
template<class T=int, class T2> void func2(T a, T2 b){}

//可变参数的模板函数
template<class ... T> //T叫模板参数包
void func(T... args)//args叫函数参数包
{  //获取可变参数的个数
   cout << "num = " << sizeof...(args) << endl;}
int main()
{
    func<int>(10);
    func<int, int>(10, 20);
    func<char, int>(10, 'a');
    func<char, char *, int>('a', "abc", 250);
    return 0;
}

/递归终止函数2
template<class T>
void debug(T tmp)
{
    cout << "tmp = " << tmp <<  endl;
}

//可变参数的模板函数
//参数包展开函数
template<class T1, class ... T2>
void debug(T1 first, T2... last)
{
    cout << first << endl;
    //递归调用函数本身
    debug(last...);
}

int main()
{
  debug(1, 2, 3, 4);
    /*
    函数递归调用过程:
    debug(1, 2, 3, 4);
    debug(2, 3, 4);
    debug(3, 4);
    debug(4);
     */

    return 0;
}

3.右值和移动函数

右值表示字面常量、表达式、函数的非引用返回值等,也就是不能取地址的值
	左值引用不能引用常数,const修饰的数和const int&类型
	int a;
	const aa=1int &b = a; //ok
   // int&bb=aa;//err
    //int &c = 1; //err
    int &s=a;
    const int &d = a; //ok
    const int &e = 1; //ok
    const int &f = func();  //ok
    const int tmp = 10;
    const int &g = tmp;
    //int& bbb=g;//err
    //const int & 万能引用

//右值引用
    int && a = 10;
    int && b = func02();//返回值不是引用的函数属于右值
	int && c =a;//右值引用右值引用
    int i = 10;
    int j = 20;
    int && c = i+j;//i+j相当于函数调用的返回值
    
    int k = 10;
    int && d = k; //err, 把一个左值赋值给一个右值引用
    
    int a = 10; //a为左值
    //int && b = a; //err, 左值不能绑定到右值引用
    int && c = std::move(a);    //std::move将一个左值转换为右值
    
转移构造函数和转移赋值函数,通过浅拷贝将临时对象被新的指针指向,这样函数返回值就不会被delete掉。
class MyString
{
public:
    MyString(const char *tmp = "abc")
    {//普通构造函数
        len = strlen(tmp);  //长度
        str = new char[len+1]; //堆区申请空间
        strcpy(str, tmp); //拷贝内容

        cout << "普通构造函数 str = " << str << endl;
    }

    MyString(const MyString &tmp)
    {//拷贝构造函数
        len = tmp.len;
        str = new char[len + 1];
        strcpy(str, tmp.str);

        cout << "拷贝构造函数 tmp.str = " << tmp.str << endl;
    }

    //移动构造函数
    //参数是非const的右值引用
    MyString(MyString && t)
    {
        str = t.str; //拷贝地址,没有重新申请内存
        len = t.len;

        //原来指针置空
        t.str = NULL;
        cout << "移动构造函数" << endl;
    }

    MyString &operator= (const MyString &tmp)
    {//赋值运算符重载函数
        if(&tmp == this)
        {
            return *this;
        }

        //先释放原来的内存
        len = 0;
        delete []str;

        //重新申请内容
        len = tmp.len;
        str = new char[len + 1];
        strcpy(str, tmp.str);

         cout << "赋值运算符重载函数 tmp.str = " << tmp.str << endl;

        return *this;

    }

    //移动赋值函数
    //参数为非const的右值引用
    MyString &operator=(MyString &&tmp)
    {
        if(&tmp == this)
        {
            return *this;
        }

        //先释放原来的内存
        len = 0;
        delete []str;

        //无需重新申请堆区空间
        len = tmp.len;
        str = tmp.str; //地址赋值
        tmp.str = NULL;

        cout << "移动赋值函数\n";

        return *this;
    }

    ~MyString()
    {//析构函数
        cout << "析构函数: ";
        if(str != NULL)
        {
            cout << "已操作delete, str =  " << str;
            delete []str;
            str = NULL;
            len = 0;

        }
        cout << endl;
    }

private:
    char *str = NULL;
    int len = 0;
};

forward保持传入参数的int& int&&const int&属性
MyString func() //返回普通对象,不是引用
{
    MyString obj("mike");

    return obj;//我们创建的obj是局部的,在函数返回后就会被销毁掉,
			   //其实我们返回的时候是拷贝了一个新的临时对象,我们接收临时对象时,
			   //可以使用右值引用接收,这样匿名对象就不会被销毁掉。
}

int main()
{
	MyString &&tmp1 = func(); //右值引用接收,函数返回值是右值,调用移动构造函数
    MyString tmp("abc"); //实例化一个对象
    tmp = func();//调用移动赋值函数
    return 0;

}

template<class T> void func(const T &)
{
    cout << "const T &" << endl;
}

template<class T> void func(T &)
{
    cout << "T &" << endl;
}

template<class T> void func(T &&)
{
    cout << "T &&" << endl;
}


template<class T> void forward_val(T &&tmp) //参数为右值引用
{
    //std::forward保存参数的左值、右值属性
    func( std::forward<T>(tmp) ); //定义
}

int main()
{
    int a = 0;
    const int &b = 1;
    //需要给forward_val()重载2个版本, const T &, T &
    forward_val(a); //"T &"
    forward_val(b);//const T &,
    forward_val(111);//T &&,虽然const int&也会引用常数,但我们优先调用形参类型是int&&的函数
    return 0;
}

4.智能指针

unique指针
	unique_ptr<int> up1(new int(11));
	unique_ptr<int> up2 = std::move(up1);
    cout << "*up2 = " << *up2 << endl; 

    unique_ptr<int> up1(new int(11));
    //释放控制权,不释放堆区内存
    int * p = up1.release();
    cout << *p << endl;
    //cout << *up1 << endl; //err
    delete p;

  unique_ptr<Test> up2(new Test); //无需释放,自动释放
  //人为指定释放堆区空间
  up2 = nullptr;  //1
  up2 = NULL;     //2
  up2.reset();
  up2.reset();释放智能指针其实就是把堆区的指针数变成零,所以我们多次将指针指向null也不会报错,因为系统会当指针数为0的时候就释放内容了。

shared_ptr
	shared_ptr<int> sp1(new int(11));
    shared_ptr<int> sp2 = sp1; //拷贝构造, 有2个对象和堆区空间绑定
    cout << "num = " << sp2.use_count() << endl; //打印计数器
    //释放sp1, 只是计数器减去1, 堆区空间没有释放
    sp1.reset();
    cout << "num = " << sp2.use_count() << endl; //打印计数器
    cout << *sp2 << endl;
    //cout << *sp1 << endl;//err, 释放sp1和堆区空间的绑定,无法通过sp1操作堆区内容
    //释放sp2, 只是计数器减去1, 当计数器的值为0, 堆区空间就是释放
    sp2.reset();
    cout << "num = " << sp2.use_count() << endl; //打印计数器
 	
weak_ptr: 
weak_ptr<int> wp = p1;
wp.use_count();
wp.lock(); //当堆区指针数为0时,释放堆区,wp.lock()改指向null
int main()
{
    shared_ptr<int> p1(new int(11));
    shared_ptr<int> p2 = p1; //有2个对象绑定堆区内容

    weak_ptr<int> wp = p1;

    cout << "num = " << p1.use_count() << endl; //打印计数器
    cout << "num = " << wp.use_count() << endl; //打印计数器

    //weak_ptr虽然不和堆区空间绑定,可以通过lock函数获取shared_ptr<int>对象
    shared_ptr<int> p3 = wp.lock(); //有3个对象绑定堆区内容
    cout << "num2 = " << p1.use_count() << endl; //打印计数器
    cout << "num2 = " << wp.use_count() << endl; //打印计数器

    cout << *p1 << ", " << *p2 << ", " << *p3 << endl;
    //cout << *wp << endl; //err, 没有重载 * 和 ->
    p1.reset();
    p2.reset();
    p3.reset();
    cout << "num3 = " << p1.use_count() << endl; //打印计数器
    cout << "num3 = " << wp.use_count() << endl; //打印计数器

    //当堆区空间释放后,wp.lock()获取的返回值为nullptr
    shared_ptr<int> tmp = wp.lock();
    if(tmp == nullptr)
    {
        cout << "堆区空间已经释放\n";
    }


    return 0;
}

5.函数绑定

#include <iostream>
#include <functional> //std::function
using namespace std;

//1、普通函数
void func()
{
    cout << __func__ << endl;
}

//2、类中静态函数
class Test
{
public:
    static int test_func(int a)
    {
        cout << __func__ << "(" << a << ") ->: ";
        return a;
    }
};

//3、类中的仿函数
class MyFunctor
{
public:
    int operator()(int a)
    {
        cout << __func__ << "(" << a << ") ->: ";
        return a;
    }
};

int main()
{
    //1、绑定普通函数
    function<void(void)> f1 = func;
    f1(); //等价于 func()

    //2、绑定类中的静态函数
    function<int(int)> f2 = Test::test_func;
    cout << f2(10) << endl; // Test::test_func(10)

    //3、绑定类中的仿函数,绑定对象, 仿函数调用obj()
    MyFunctor obj;
    function<int(int)> f3 = obj;
    cout << f3(22) << endl;


    return 0;
}


void func(int x, int y)
{
    cout << x << " " << y << endl;
}

int main()
{
    bind(func, 11, 22)(); //输出11 22

    //std::placeholders::_1, 函数调用时,被第一个参数替换
    //std::placeholders::_2, 函数调用时,被第二个参数替换
    //输出11 22
    bind(func, std::placeholders::_1, std::placeholders::_2)(11, 22);

    using namespace std::placeholders;
    bind(func, 11, _1)(22, 33, 44); //输出 11 22, 后面的33, 44没有作用

    bind(func, _2, _1)(11, 22); //输出 22 11

    //bind(func, _2, 22)(11); //err, 没有第2个参数
    bind(func, _2, 22)(11, 0);    //0 22
    bind(func, _3, 22)(11, 1, 3); //3 22
    return 0;
}


class Test
{
public:
    void func(int x, int y)
    {//成员函数
        cout << x << " " << y << endl;
    }

    int a; //成员变量
};

int main()
{
    Test obj; //创建对象

    //绑定成员函数
    function<void(int, int)> f1 = bind(&Test::func, &obj, _1, _2);
    f1(11, 22); //obj.func(11, 22)

    //绑定成员变量
    function<int &()> f2 = bind(&Test::a, &obj);
    f2() = 111; //obj.a = 111;
    cout << "obj.a = " << obj.a << endl;

    return 0;
}

6.lambda

空。没有使用任何函数对象参数。
=。函数体内可以使用 lambda 所在作用范围内所有可见的局部变量(包括 lambda 所在类的 this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局 部变量)。
  &。函数体内可以使用 lambda 所在作用范围内所有可见的局部变量(包括 lambda 所在类的 this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所 有局部变量)。
   this。函数体内可以使用 lambda 所在类中的成员变量。 
   a。将 a 按值进行传递。按值进行传递时,函数体内不能修改传递进来的 a 的拷贝, 因为默认情况下函数是 const 的。要修改传递进来的 a 的拷贝,可以添加 mutable 修饰符。
    &a。将 a 按引用进行传递。 
    a, &b。将 a 按值进行传递,b 按引用进行传递。 
    =&a, &b。除 a 和 b 按引用进行传递外,其他参数都按值进行传递。
    &, a, b。除 a 和 b 按值进行传递外,其他参数都按引用进行传递。
int tmp = 1;
class Test
{
public:
    int i = 0;

    void func()
    {
        int a = 10;
        //err, []为空,没有捕获任何变量
        //auto f1 = [](){ cout << i << endl; };

        auto f1 = [=]()
            {
                cout << i << endl;
                cout << tmp << endl;

            };
        auto f2 = [&](){ cout << i << endl; };

        //只是捕获类成员变量、全局变量, 不能捕获局部变量
        auto f3 = [this]()
            {
                cout << i << endl;
                cout << tmp << endl;
                //cout << a << endl; //err
            };
    }

};

int main()
{
    int a = 0;
    int b = 0;
    int c = 0;

    auto f1 = [](){ };
    //a, b以值传递方式
    auto f2 = [a, b](){ cout << a << ", " << b << endl; };
    auto f3 = [a, b](int x, int y)
        {
            cout << a << ", " << b << endl;
            cout << "x = " << x << endl;
            cout << "y = " << y << endl;

        };
    //f3(10, 20);

    //以值传递方式传给lambda表达式
    auto f4 = [=]{cout << a << ", " << b << endl;};

    //以引用方式捕获外部的变量
    auto f5 = [&]{cout << a << ", " << b << endl;};

    //a以值传递, 其它以引用方式传递
    auto f6 = [&, a]{cout << a << ", " << b << endl;};

    //a以引用传递,其它值传递
    auto f7 = [=, &a]{cout << a << ", " << b << endl;};

    //默认情况下,lambda函数,以const修饰函数体, 值传递无法修改, 想修改加mutable
    auto f8 = [=]() mutable
        {
            a++;
            cout << tmp << endl;
        };
    return 0;
}



//仿函数,重载operator()
class MyFunctor
{
public:
    MyFunctor(int i): r(i) {} //构造函数

    //仿函数,重载operator()
    int operator() (int tmp)
    {
        return tmp+r;
    }

private:
    int r;
};

int main()
{
    int tmp = 10;

    //仿函数的调用
    MyFunctor obj(tmp); //调用构造函数

    //调用仿函数
    cout << "result1 = " << obj(1) << endl;

    //仿函数是编译器实现lambda的一种方式
    //lambda表达式
    auto f = [&](int t)
        {
            return tmp+t;
        };
    cout << "result2 = " << f(1) << endl;


    return 0;
}


//使用 std::function 和 std::bind 来存储和操作 lambda 表达式 
function<int(int)> f1 = [](int a) { return a; }; 
function<int()> f2 = bind([](int a){ return a; }, 123);

lambda优势
 for_each(largeNums.begin(), largeNums.end(),
        [](int &n)
        {
            cout << n << ", ";
        }
    );
    cout << endl;

7.类型转换

char a = 'a';
int b = static_cast<char>(a);//正确,将char型数据转换成int型数据

double *c = new double;
void *d = static_cast<void*>(c);//正确,将double指针转换成void指针

int e = 10;
const int f = static_cast<const int>(e);//正确,将int型数据转换成const int型数据

const int g = 20;
int *h = static_cast<int*>(&g);//编译错误,static_cast不能转换掉g的const属性


if(Derived *dp = static_cast<Base *>(bp)){//下行转换是不安全的
  //使用dp指向的Derived对象  
}
else{
  //使用bp指向的Base对象  
}

if(Base*bp = static_cast<Derived *>(dp)){//上行转换是安全的
  //使用bp指向的Derived对象  
}
else{
  //使用dp指向的Base对象  
}


const_cast将常量指针常量引用转换成非常量指针引用:在通过非常量指针修改内存值。
①常量指针被转化成非常量的指针,并且仍然指向原来的对象;
②常量引用被转换成非常量的引用,并且仍然指向原来的对象;
③const_cast一般用于修改底指针。如const char *p形式。

const int g = 20;
int *h = const_cast<int*>(&g);//去掉const常量const属性
*h=40//*h是非常量指针,指向原来的内存,所以可以改变其指向内存的值

const int g = 20;
int &h = const_cast<int &>(g);//去掉const引用const属性

const char *g = "hello";
char *h = const_cast<char *>(g);//去掉const指针const属性


dynamic_cast
Derived *dp = dynamic_cast<Base *>(bp)
父类指针强转成子类且父类存在虚函数时,建议使用dynamic,可以检测是否存在越界的风险


reinterpret_cast强转
int n=1int *p=(int*)n;
int *p=reinterpret_cast<int*>(n);
Derived *dp=reinterpret_cast<Base*>(bp);

8.字符串和数值转换

string pi = "pi is " + to_string(3.1415926);
int intpi = stoi(pi);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值