欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

c++11的一些新特(持续补充)

程序员文章站 2022-04-05 08:42:52
...

1. auto关键字

在c语言中,auto用于修饰局部变量,也称之为自动变量

void func()
{
    auto int a;     //等价于int a
}

在c++11中,auto根据用户的初始化内容自动推导其类型:

#include <iostream>
#include <string>
#include <vector>

struct mt {
    int a;
    std::string str;
};

char mfunc()
{
    return 'v';
}

void mfunc2(std::vector<int>& vec)
{
    //auto自动推导容器模板的迭代器
    for (auto iter = vec.begin(); iter < vec.end(); ++iter) {
        std::cout << *iter << " ";
    }
    std::cout << std::endl;
}
int main(void)
{
    auto a = 11;    //auto自动推导a为int类型
    auto pa = &a;   //auto自动推导pa为int类型的指针

    std::cout << "*pa = " << *pa << std::endl;

    mt t1;
    auto t2 = t1;   //auto自动推导t2为自定义类型mt

    auto r = mfunc();   //r为char

    std::vector<int> vec;
    for (auto i = 0; i < 10; i++) {
        vec.push_back(i);
    }
    mfunc2(vec);

    return 0;
}

注意:
(1)用auto定义变量时必须使用初始化(而不是赋值),因为auto就是根据初始值内容来分配内存空间的;
(2)auto变量不能作为自定义类型的成员变量:

struct mt1 {
    auto i;
};

(3)不能定义auto数组:

auto marr[2] = {1, 2};

(4)模板实例化类型不能是auto类型:

std::vector<auto> vec1 = {1, 2, 3};

(5)函数形参不可以是auto变量

void mfunc3(auto i)
{
}

2. decltype关键字

decltype可通过一个变量获取其类型,这样我们自定义的匿名类型在声明之后还可以继续用来定义变量:

#include <iostream>
#include <typeinfo>

using namespace std;

int main()
{
    struct {    /* 这是匿名结构体类型 */
        int a;
    } ms;
    decltype(ms) ms2;   /* 获取ms的类型后定义ms变量 */
    int a;

    /* 打印类型的名字,这个在不同编译器有不同的表达 */
    cout << typeid(a).name() << endl;
    cout << typeid(ms2).name() << endl;

    return 0;
}

3. 追踪函数返回值类型

用auto关键字声明函数的返回值类型,需要用”->”指定具体是什么类型:

auto mfunc(int x, int y)->int
{
    return x + y;
}

利用decltype代替代码中的int:

auto mfunc(int x, int y)->decltype(x + y)
{
    return x + y;
}

这即是追踪函数返回值类型,将其应用于泛型编程中:

template<typename T1, typename T2>
auto mfunc1(T1 t1, T2 t2)->decltype(t1 * t2)
{
    return t1 * t2;
}

因为t1、t2是泛指类型,所以程序员无法为mfunc2()指定二者相乘的类型,利用函数返回值追踪就很容易实现。

4. 直接初始化类中的成员变量

在c++11之前,对类中的成员变量的初始化要么是放在构造函数中,要么是在构造函数的初始化列表中:

class t1 {
public:
    t1(int a) : name("kyo") {
        age = a;
    }
    std::string name;
    int age;
};

c++11则支持直接初始化:

class tt {
public:
    int num = 6;
    std::string remarks {"hello"};
    t1 t = 6;
};

5. 列表初始化

在c++11之前的列表初始化运用如下:

struct st {
    int a;
    std::string str;
};
st s1 = {'a', "hello"};

上面是自定义类型的列表初始化,c++11对于列表初始化还支持于内置类型:

struct st {
    int a;
    std::string str;
};

st s1 {'a', "hello"};
int a {6};
char arr[] {1, 2, 3, 3, 5, 6};

运用于数组时显得十分方便。

6. 基于范围的for循环

int arr[] {2, 4, 6, 8};
int cnt = sizeof(arr) / sizeof(arr[0]);

在c++之前的for循环遍历数组:

for (int i = 0; i < cnt; ++i) {
    int tmp = arr[i];
    std:: cout << tmp << " ";
}
std::cout << std::endl;

在c++11等等效写法为:

for (int tmp : arr) {
    std:: cout << tmp << " ";
}
std::cout << std::endl;

运用于引用同理:

for (int i = 0; i < cnt; ++i) {
    int &tmp = arr[i];
    tmp++;
    std:: cout << tmp << " ";
}
std::cout << std::endl;

for (int &tmp : arr) {
    tmp++;
    std:: cout << tmp << " ";
}
std::cout << std::endl;

需要注意的是,上面c++11中的语法只能运用于数组大小可知的场合

7. 静态断言

assert()是c/c++用的断言函数,如下代码:

#include <assert.h>
int main(void)
{
    bool val = true;

    assert(val == false);

    printf("running...\n");

    return 0;
}

assert(val == false)代码语句表示,程序运行时若条件为真则继续往下执行,若条件为假则中断程序并提示错误:
c++11的一些新特(持续补充)
c++11提供了静态断言函数,在程序编译阶段就可以中断程序。需要强调的是,因为需要在编译阶段就需要作出断言结果,断言的条件只能是常量表达式。如下用于判断系统是多少位:

int main(void)
{
    static_assert(sizeof(void* ) == 4, "local system is 64bit\n");

    printf("running...\n");

    return 0;
}

在64位的操作系统上编译:
c++11的一些新特(持续补充)

8. noexcept关键字

noexcept用于声明函数不会抛出异常(君子协议):

int func2() noexcept
{
    return 0;
}

跟下面的语义是一致的:

int func2() throw()
{
    throw 1;
    return 0;
}

c++11的noexcept在编译阶段会做出优化。

9. 强类型枚举

int main(void)
{
    enum status0 {
        success,
        failed
    };

    enum status1 {
        ok,
        failed
    };
    return 0;
}

编译:
c++11的一些新特(持续补充)
报错,这是因为枚举类型在同一个作用域中不能出现两次,即重复定义了。c++11中支持用class或struct关键字对enum作用域加以划分,且在使用该enum类型时需要指定作用域,称为强类型枚举:

enum class status0 {
    success,
    failed
};

enum class status1 {
    ok,
    failed
};
status0 val = status0::success; //需要指定作用域

强类型枚举还支持指定成员变量的类型:

enum class status0 {
    success,
    failed
};

enum class status1:char {
    ok,
    failed
};

status0 val = status0::success; 

printf("%d\n", sizeof(status0::failed)); //默认是4字节,跟c语言的枚举类型一样默认是int类型
printf("%d\n", sizeof(status1::ok));     //1字节

10. nullptr

int func(int a)
{
    printf("func(int )\n");
    return 0;
}

int func(int* p)
{
    printf("func(int* )\n");
    return 0;
}

int main(void)
{
    func(0);
    func(NULL);

    return 0;
}

如上重载函数,调用者希望func(0)调用的是func(int)原型函数,func(NULL)则调用func(int* ),然而:
c++11的一些新特(持续补充)
编译器报错,func(NULL)不能匹配到调用函数,这是因为NULL其实质是一个0地址值。为了明确NULL指针,c++11引入了nullptr:

func(0);
func(nullptr);

c++11的一些新特(持续补充)

11. constexpr常量表达式

常量表达的计算结果是发生在编译阶段,而非运行阶段。

const int get_num()
{
    return 6;
}

int main(void)
{
    enum Num {d1 = get_num(), d2};
    return 0;
}

我们知道enum成员变量必须是一个常量,然而通过get_num()函数获取得值显然是通过非常量表达式的方法获取的,即必须在运行时才能知道d1的值,所以报错:
c++11的一些新特(持续补充)
关键字constexpr可用于修饰表达式为一常量表达式:

constexpr int get_num()
{
    return 6;
}

这样就编译正常了。同时还可以这么玩:

constexpr int get_num()
{
    return 6;
}

int main(void)
{

    constexpr int n = get_num();
    enum Num {d1 = n, d2};

    return 0;
}

需要强调,constexpr声明函数的限制:
(1)除了函数中只能有一个语句且该语句h是return语句;
(2)函数必须有返回值且,且只能返回常量/常量表达式,不可以是全局数据。
(3)在使用前必须先定义,注意是定义,单单声明不行;

相关标签: c++11 auto