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

1. auto关键字

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

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

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

#include 
#include 
#include 

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 
#include 

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提供了静态断言函数,在程序编译阶段就可以中断程序。需要强调的是,因为需要在编译阶段就需要作出断言结果,断言的条件只能是常量表达式。如下用于判断系统是多少位:

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

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

    return 0;
}

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

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的一些新特(持续补充)_第2张图片
报错,这是因为枚举类型在同一个作用域中不能出现两次,即重复定义了。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的一些新特(持续补充)_第3张图片
编译器报错,func(NULL)不能匹配到调用函数,这是因为NULL其实质是一个0地址值。为了明确NULL指针,c++11引入了nullptr:

func(0);
func(nullptr);

c++11的一些新特(持续补充)_第4张图片

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的一些新特(持续补充)_第5张图片
关键字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/C++编程,c++11,auto)