IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++11新特性 -> 正文阅读

[C++知识库]C++11新特性

1 新增关键字及新用法

========================================================================================================

1.1 auto关键字及用法

#include <iostream>
#pragma once
//auto 可以做什么
//auto并没有让C++成为弱类型语言,也没有弱化变量什么,只是使用auto的时候,编译器根据上下文情况,确定auto变量的真正类型。
auto AddTest(int a, int b) 
{
    return a + b;
}
//是的,你没看错auto在C++14中可以作为函数的返回值,因此auto AddTest(int a, int b)的定义是没问题的。
//auto不能做什么?
//auto作为函数返回值时,只能用于定义函数,不能用于声明函数。
// class Test
// {
// public:
//     auto TestWork(int a ,int b);
// };
//如下函数中,在引用头文件的调用TestWork函数是,编译无法通过。
//但如果把实现写在头文件中,可以编译通过,因为编译器可以根据函数实现的返回值确定auto的真实类型。
//如果读者用过inline类成员函数,这个应该很容易明白,此特性与inline类成员函数类似。
int main()
{   
    auto index = 10;
    auto str = "abc";
    auto ret = AddTest(1,2);
    std::cout << "index:" << index << std::endl;
    std::cout << "str:" << str << std::endl;
    std::cout << "res:" << ret << std::endl;
}


1.2 nullptr关键字及用法

#include <iostream>
/*
为什么需要nullptr? NULL有什么毛病?
NULL在c++里表示空指针,看到问题了吧,我们调用test.TestWork(NULL),其实期望是调用的是
void TestWork(int* index),但结果调用了void TestWork(int index)。但使用nullptr的时候,
我们能调用到正确的函数。
*/
class Test
{
public:
    void TestWork(int index)
    {
        std::cout << "TestWork 1" << std::endl;
    }
    void TestWork(int* index)
    {
        std::cout << "TestWork 2" << std::endl;
    }
};
int main()
{
    Test test;
    // test.TestWork(NULL);
    test.TestWork(nullptr);
}在这里插入代码片

1.3 for循环语法

#include <iostream>
//以下用法不仅仅局限于数据,STL容器都同样适用。
int main()
{
    int numbers[] = { 1,2,3,4,5 };
    std::cout << "numbers:" << std::endl;
    for (auto number : numbers)
    {
        std::cout << number << std::endl;
    }
    return 0;
}

========================================================================================================

2 新增STL容器

2.1 array

#include <array>
#include <iostream>
//个人觉得std::array跟数组并没有太大区别,对于多维数据使用std::array,个人反而有些不是很习惯吧。
//std::array相对于数组,增加了迭代器等函数(接口定义可参考C++官方文档)。
int main(){
    std::array<int, 4> arrayDemo = {1,2,3,4}; //这种赋值方法适合C++11中的大部分容器
    std::cout << "arrayDemo:" << std::endl;
    for(auto itor : arrayDemo)
    {
        std::cout << itor << std::endl;
    }
    int arrayDemoSize = sizeof(arrayDemo);
    std::cout << "arrayDemo size:" << arrayDemoSize << std::endl;
    return 0;
}

在这里插入代码片

2.2 forward_list

#include <forward_list>
#include <iostream>
//std::forward_list为c++新增的线性表,与list区别在于它是单向链表。
//我们在学习数据结构的时候都知道,链表在对数据进行插入和删除是比顺序存储的线性表有优势,
//因此在插入和删除操作频繁的应用场景中,使用list和forward_list
//比使用array、vector和deque效率要高很多。
int main()
{
    std::forward_list<int> numbers = {1,2,3,4,5,4,4};
    std::cout << "numbers:" << std::endl;
    for (auto number : numbers)
    {
        std::cout << number << std::endl;
    }
    numbers.remove(4);
    std::cout << "numbers after remove:" << std::endl;
    for (auto number : numbers)
    {
        std::cout << number << std::endl;
    }
    return 0;
}
在这里插入代码片

2.3 unordered_map

#include <iostream>
#include <string>
#include <unordered_map>
//std::unordered_map与std::map用法基本差不多,但STL在内部实现上有很大不同,
//std::map使用的数据结构为二叉树,而std::unordered_map内部是哈希表的实现方式,
//哈希map理论上查找效率为O(1)。但在存储效率上,哈希map需要增加哈希表的内存开销。
int main()
{
    std::unordered_map<std::string, std::string> mymap =
    {
        { "house","maison" },
        { "apple","pomme" },
        { "tree","arbre" },
        { "book","livre" },
        { "door","porte" },
        { "grapefruit","pamplemousse" }
    };
    unsigned n = mymap.bucket_count();
    std::cout << "mymap has " << n << " buckets.\n";
    for (unsigned i = 0; i<n; ++i) 
    {
        std::cout << "bucket #" << i << " contains: ";
        for (auto it = mymap.begin(i); it != mymap.end(i); ++it)
            std::cout << "[" << it->first << ":" << it->second << "] ";
        std::cout << "\n";
    }
    return 0;
}
在这里插入代码片

2.4 unordered_set

//std::unordered_set的数据存储结构也是哈希表的方式结构,
//除此之外,std::unordered_set在插入时不会自动排序,这都是std::set表现不同的地方。
#include <string>
#include <unordered_set>
#include <set>
int main()
{
    std::unordered_set<int> unorder_set;
    unorder_set.insert(7);
    unorder_set.insert(5);
    unorder_set.insert(3);
    unorder_set.insert(4);
    unorder_set.insert(6);
    std::cout << "unorder_set:" << std::endl;
    for (auto itor : unorder_set)
    {
        std::cout << itor << std::endl;
    }
    std::set<int> set;
    set.insert(7);
    set.insert(5);
    set.insert(3);
    set.insert(4);
    set.insert(6);
    std::cout << "set:" << std::endl;
    for (auto itor : set)
    {
        std::cout << itor << std::endl;
    }
}在这里插入代码片

========================================================================================================

3 多线程

3.1 thread

#include <iostream>
#include <thread>

// 在C++11以前,C++的多线程编程均需依赖系统或第三方接口实现,一定程度上影响了代码的移植性。
//C++11中,引入了boost库中的多线程部分内容,形成C++标准,形成标准后的boost多线程编程部分接口基本没有变化,
//这样方便了以前使用boost接口开发的使用者切换使用C++标准接口,把容易把boost接口升级为C++接口。

//  std::thread为C++11的线程类,使用方法和boost接口一样,非常方便,同时,C++11的std::thread解决了
//boost::thread中构成参数限制的问题,我想着都是得益于C++11的可变参数的设计风格。

void threadfun1()
{
    std::cout << "threadfun1 - 1\r\n" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "threadfun1 - 2" << std::endl;
}

void threadfun2(int iParam, std::string sParam)
{
    std::cout << "threadfun2 - 1" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "threadfun2 - 2" << std::endl;
}

int main()
{
    std::thread t1(threadfun1);
    std::thread t2(threadfun2, 10, "abc");
    t1.join();
    std::cout << "join" << std::endl;
    t2.detach();
    std::cout << "detach" << std::endl;
}
/*
有以上输出结果可以得知,t1.join()会等待t1线程退出后才继续往下执行,t2.detach()并不会。
把detach字符输出后,主函数退出,threadfun2还未执行完成,但是在主线程退出后,t2的线程也被已经被强退出。

在声明一个std::thread对象之后,都可以使用detach和join函数来启动被调线程,区别在于两者是否阻塞主调线程。
(1)当使用join()函数时,主调线程阻塞,等待被调线程终止,然后主调线程回收被调线程资源,并继续运行;
(2)当使用detach()函数时,主调线程继续运行,被调线程驻留后台运行,主调线程无法再取得该被调线程的控制权。
    当主调线程结束时,由运行时库负责清理与被调线程相关的资源。
*/

3.2 atomic

#include <atomic>
#include <stdio.h>

/*

std::atomic为C++11封装的原子数据类型。
什么是原子数据类型?
从功能上看,简单地说,原子数据类型不会发生数据竞争,能直接用在多线程中
而不必我们用户对其进行添加互斥资源锁的类型。
从实现上,大家可以理解为这些原子类型内部自己加了锁。
我们下面通过一个测试例子说明原子类型std::atomic_int的特点。
下面例子中,我们使用10个线程,把std::atomic_int类型的变量iCount从100减到1。
*/

std::atomic_bool bIsReady = false;
std::atomic_int iCount = 100;
void threadfun1()
{
    if (!bIsReady) {
        std::this_thread::yield();
    }
    while (iCount > 0)
    {
        printf("iCount:%d\r\n", iCount--);
    }
}

int main()
{
    std::atomic_bool b;
    std::list<std::thread> lstThread;
    for (int i = 0; i < 10; ++i)
    {
        lstThread.push_back(std::thread(threadfun1));
    }
    for (auto& th : lstThread)
    {
        th.join();
    }
}

//  从上面的结果可以看到,iCount的最小结果都是1,
//单可能不是最后一次打印,没有小于等于0的情况,大家可以代码复制下来多运行几遍对比看看。
在这里插入代码片

3.3 condition_variable

// webset address: http://www.cplusplus.com/reference/condition_variable/condition_variable/%20condition_variable
// condition_variable example
#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void print_id(int id) {
    std::unique_lock<std::mutex> lck(mtx);
    while (!ready) cv.wait(lck);
    // ...
    std::cout << "thread " << id << '\n';
}

void go() {
    std::unique_lock<std::mutex> lck(mtx);
    ready = true;
    cv.notify_all();
}

int main()
{
    std::thread threads[10];
    // spawn 10 threads:
    for (int i = 0; i<10; ++i)
        threads[i] = std::thread(print_id, i);

    std::cout << "10 threads ready to race...\n";
    go();                       // go!

    for (auto& th : threads) th.join();

    return 0;
}在这里插入代码片

========================================================================================================

4智能指针内存管理

4.1 shared_ptr

/*
在内存管理方面,C++11的std::auto_ptr基础上,移植了boost库中的智能指针的部分实现,如std::shared_ptr、std::weak_ptr等,
当然,想boost::thread一样,C++11也修复了boost::make_shared中构造参数的限制问题。把智能指针添加为标准,个人觉得真的非常方便,
毕竟在C++中,智能指针在编程设计中使用的还是非常广泛。
什么是智能指针?网上已经有很多解释,个人觉得“智能指针”这个名词似乎起得过于“霸气”,很多初学者看到这个名词就觉得似乎很难。
简单地说,智能指针只是用对象去管理一个资源指针,同时用一个计数器计算当前指针引用对象的个数,当管理指针的对象增加或减少时,
计数器也相应加1或减1,当最后一个指针管理对象销毁时,计数器为1,此时在销毁指针管理对象的同时,也把指针管理对象所管理的指针进行delete操作。
*/
#include <iostream>
#include <memory>
using namespace std;

//std::shared_ptr包装了new操作符动态分配的内存,可以自由拷贝复制,基本上是使用最多的一个智能指针类型。

class Test
{
public:
    Test()
    {
        cout << "Test()" << endl;
    }

    ~ Test()
    {
        cout << "~Test()" << endl;
    }

};

int main()
{
    shared_ptr<Test> p1 = make_shared<Test>(); //make_shared封装了new方法
    cout << "1 ref: " << p1.use_count() << endl;
    {
        shared_ptr<Test> p2 = p1;
        cout << "2 ref:" << p1.use_count() << endl;
    }
    //出了作用域,p2 销毁,就只剩下p1指向对象 Test
    cout << "3 ref:" << p1.use_count() << endl;
    return 0;
}

/*
从上面代码的运行结果,需要读者了解的是:
1、std::make_shared封装了new方法,boost::make_shared之前的原则是既然释放资源
delete由智能指针负责,那么应该把new封装起来,否则会让人觉得自己调用了new,但没有调用
delete,似乎与谁申请,谁释放的原则不符。C++也沿用了这一做法。
2、随着引用对象的增加std::shared_ptr<Test> p2 = p1,指针的引用计数有1变为2,
当p2退出作用域后,p1的引用计数变回1,当main函数退出后,p1离开main函数的作用域,
此时p1被销毁,当p1销毁时,检测到引用计数已经为1,就会在p1的析构函数中调用delete之前std::make_shared创建的指针。
*/在这里插入代码片

4.2 weak_ptr相互引用问题实例

/*
std::weak_ptr网上很多人说其实是为了解决std::shared_ptr在相互引用的情况下出现的问题而存在的,
C++官网对这个只能指针的解释也不多,那就先甭管那么多了,让我们暂时完全接受这个观点。
std::weak_ptr有什么特点呢?与std::shared_ptr最大的差别是在赋值时,不会引起智能指针计数增加。

我们下面将继续如下两点:
  1、std::shared_ptr相互引用会有什么后果;
  2、std::weak_ptr如何解决第一点的问题。
*/

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

class TestB;
class TestA
{
public:
    TestA()
    {
        std::cout << "TestA()" << std::endl;
    }

    void ReferTestB(shared_ptr<TestB> test_ptr)
    {
        m_TestB_Ptr = test_ptr;
    }

    ~TestA()
    {
        std::cout << "~TestA()" << std::endl;
    }
private:
    shared_ptr<TestB> m_TestB_Ptr; //testB 的智能指针

}; 


class TestB
{
public:
    TestB()
    {
        std::cout << "TestB()" << std::endl;
    }

    void ReferTestB(std::shared_ptr<TestA> test_ptr)
    {
        m_TestA_Ptr = test_ptr;
    }
    ~TestB()
    {
        std::cout << "~TestB()" << std::endl;
    }
    std::shared_ptr<TestA> m_TestA_Ptr; //TestA的智能指针
};


int main()
{
    std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>();
    std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>();
    ptr_a->ReferTestB(ptr_b);
    ptr_b->ReferTestB(ptr_a);
    return 0;
}

/*
大家可以看到,上面代码中,我们创建了一个TestA和一个TestB的对象,但在整个main函数都运行完后,
都没看到两个对象被析构,这是什么问题呢?
原来,智能指针ptr_a中引用了ptr_b,同样ptr_b中也引用了ptr_a,在main函数退出前,
ptr_a和ptr_b的引用计数均为2,退出main函数后,引用计数均变为1,也就是相互引用。
这等效于说:
ptr_a对ptr_b说,哎,我说ptr_b,我现在的条件是,你先释放我,我才能释放你,这是天生的,造物者决定的,改不了。
ptr_b也对ptr_a说,我的条件也是一样,你先释放我,我才能释放你,怎么办?
是吧,大家都没错,相互引用导致的问题就是释放条件的冲突,最终也可能导致内存泄漏。
*/
在这里插入代码片

4.3weak_ptr如何解决相互引用的问题

//  我们在上面的代码基础上使用std::weak_ptr进行修改:

class TestB;
class TestA
{
public:
    TestA()
    {
        std::cout << "TestA()" << std::endl;
    }
    void ReferTestB(std::shared_ptr<TestB> test_ptr)
    {
        m_TestB_Ptr = test_ptr;
    }
    void TestWork()
    {
        std::cout << "~TestA::TestWork()" << std::endl;
    }
    ~TestA()
    {
        std::cout << "~TestA()" << std::endl;
    }
private:
    std::weak_ptr<TestB> m_TestB_Ptr;
};

class TestB
{
public:
    TestB()
    {
        std::cout << "TestB()" << std::endl;
    }

    void ReferTestB(std::shared_ptr<TestA> test_ptr)
    {
        m_TestA_Ptr = test_ptr;
    }
    void TestWork()
    {
        std::cout << "~TestB::TestWork()" << std::endl;
    }
    ~TestB()
    {
        std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock();
        tmp->TestWork();
        std::cout << "2 ref a:" << tmp.use_count() << std::endl;
        std::cout << "~TestB()" << std::endl;
    }
    std::weak_ptr<TestA> m_TestA_Ptr;
};


int main()
{
    std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>();
    std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>();
    ptr_a->ReferTestB(ptr_b);
    ptr_b->ReferTestB(ptr_a);
    std::cout << "1 ref a:" << ptr_a.use_count() << std::endl;
    std::cout << "1 ref b:" << ptr_a.use_count() << std::endl;
    return 0;
}

/*
由以上代码运行结果我们可以看到:

  1、所有的对象最后都能正常释放,不会存在上一个例子中的内存没有释放的问题。
  2、ptr_a 和ptr_b在main函数中退出前,引用计数均为1,也就是说,在TestA和
      TestB中对std::weak_ptr的相互引用,不会导致计数的增加。在TestB析构函数中,
      调用std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock(),把std::weak_ptr
      类型转换成std::shared_ptr类型,然后对TestA对象进行调用。 
*/

在这里插入代码片

4.4 unique_ptr

//https://www.cnblogs.com/yocichen/p/10561615.html

/*
1) 任意时刻unique_ptr只能指向某一个对象,指针销毁时,指向的对象也会被删除
    (通过内置删除器,通过调用析构函数实现删除对象)
2)禁止拷贝和赋值(底层实现拷贝构造函数和复制构造函数 = delete),
    可以使用std::move()、unique_ptr.reset(...) 转移对象指针控制权。

(由1决定,指针发生了拷贝就违反了第一条)
*/

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

// unique_ptr::get vs unique_ptr::release
int main()
{
    std::unique_ptr<int> foo; //foo - null
    std::unique_ptr<int> bar; //bar - null
    int* p = nullptr;
    foo = std::unique_ptr<int>(new int(100));// foo - 100
    bar = std::move(foo); // foo转移给bar bar - 100 foo - null
    p = bar.get(); // p - 100 smart pointer.get()返回一个指向该对象的内置指针
    foo.reset(bar.release()); // bar 放弃指针控制权,返回指针给foo foo - 100, bar已经不存在

    cout << "foo : " << *foo << endl;
    cout << "p   : " << *p << endl;
    delete p; //记得删除,这也是使用智能指针的初衷之一---防止内存泄漏!!!

    if (bar)
        cout << "bar : " << *bar << endl;
    else
        cout << "bar已经被释放" << endl; //这里bar已经销毁了,可能会报错。

    return 0;
}在这里插入代码片

========================================================================================================

5其他

5.1 function,bind封装可执行对象

//修改前
#include <iostream>
#include "Test.h"
//假如我们的需求是让Test里面的Add由外部实现,如main.cpp里面的add函数,有什么方法呢?
//没错,我们可以用函数指针。

int add(int a,int b)
{
    return a + b;
}

//假如add实现是在另外一个类内部,这个时候std::function和std::bind就帮上忙了。


class TestAdd
{
public:
    void Add(std::function<int(int, int)> fun, int a, int b)
    {
        int sum = fun(a, b);
        std::cout << "sum:" << sum << std::endl;
    }
};


int main()
{

    Test test;
    test.Add(add, 1, 2);


    TestAdd testAdd;
    test.Add(std::bind(&TestAdd::Add, testAdd, std::placeholders::_1, std::placeholders::_2), 1, 2);

    return 0;
}


在这里插入代码片

5.2 lamda表达式

头文件test.h


//修改前
/*
Test
{
public:
    void Add()
    {
        
    }
};
*/



//假如我们的需求是让Test里面的Add由外部实现,如main.cpp里面的add函数,有什么方法呢?
//没错,我们可以用函数指针。
/*
class Test
{
public:
    typedef int(*FunType)(int, int);
    void Add(FunType fun,int a,int b)
    {
        int sum = fun(a, b);
        std::cout << "sum:" << sum << std::endl;
    }
};
*/

//假如add方法在TestAdd类内部

class Test
{
public:
    void Add(std::function<int(int, int)> fun, int a, int b)
    {
        int sum = fun(a, b);
        std::cout << "sum:" << sum << std::endl;
    }
};
在这里插入代码片

test1.cpp

#include <iostream>

int main()
{
     auto add = [](int a, int b)->int{
         return a + b;
     };
    int ret = add(1,2);
    std::cout << "ret:" << ret << std::endl;
    return 0;
 }


/*
第3至5行为lamda表达式的定义部分

[]:中括号用于控制main函数以内,lamda表达式之前的变量在lamda表达式中的访问形式;
(int a,int b):为函数的形参
->int:lamda表达式函数的返回值定义
{}:大括号内为lamda表达式的函数体。

*/在这里插入代码片

test2.cpp

#include<iostream>
#include "Test.h"
//我使用lamda表达式修改5.1中的例子看看:



int add(int a,int b)
{
    return a + b;
}

//假如add实现是在另外一个类内部,这个时候std::function和std::bind就帮上忙了。


class TestAdd
{
public:
    void Add(std::function<int(int, int)> fun, int a, int b)
    {
        int sum = fun(a, b);
        std::cout << "sum:" << sum << std::endl;
    }
};



int main()
{
    Test test;
    test.Add(add, 1, 2);

    // TestAdd testAdd;
    // test.Add(std::bind(&TestAdd::Add, testAdd, std::placeholders::_1, std::placeholders::_2), 1, 2);

    test.Add([](int a, int b)->int {
        std::cout << "lamda add fun" << std::endl;
        return a + b;
    },1,2);
    return 0;
}在这里插入代码片

5.3 左值右值问题

左值引用.cpp

int main()
{
    int a = 10; 
    int& refA = a; // refA是a的别名, 修改refA就是修改a, a是左值,左移是左值引用

    int& b = 1; //编译错误! 1是右值,不能够使用左值引用

    return 0;
}在这里插入代码片

右值引用.cpp


int&& a = 1; //实质上就是将不具名(匿名)变量取了个别名
int b = 1;
int && c = b; //编译错误! 不能将一个左值复制给一个右值引用
class A {
  public:
    int a;
};
A getTemp()
{
    return A();
}
A && a = getTemp();   //getTemp()的返回值是右值(临时变量)
在这里插入代码片

5.4 decltype 类型指示符

int a = 10;
int b = 20;
decltype(a+b) c = 50; // OK c的类型就是 a+b 的类型int在这里插入代码片

5.5 尾置返回类型

auto func(char x) -> int(*) [10];
在这里插入代码片

5.6 类对象成员的类内初始化

class ClassName
{
        public:
                int x = 10; //C++11 之前是不允许的
};

在这里插入代码片

5.7 设置构造函数为default

https://blog.csdn.net/sevenjoin/article/details/88314531

/*
在C++的类中,如果我们没有定义构造函数,编译器会为我们合成默认的无参构造函数,
如果我们定义了构造函数,则编译器就不生成默认构造函数了,但是如果我们定义构造函数同时
也希望编译器生成默认构造函数呢? C++11中可以通过在构造函数的声明中直接  =default的方式要求
编译器生成构造函数。
*/

class ClassName{
    public:
        ClassName(int x);
        ClassName()=default; // 显式要求编译器生成构造函数
};

/*
C++11中,当类中含有不能默认初始化的成员变量时,可以禁止默认构造函数的生成,
myClass()=delete;//表示删除默认构造函数
myClass()=default;//表示默认存在构造函数
当类中含有不能默认拷贝成员变量时,可以禁止默认构造函数的生成,
myClass(const myClass&)=delete;//表示删除默认拷贝构造函数,即不能进行默认拷贝
myClass & operatir=(const myClass&)=delete;//表示删除默认拷贝构造函数,即不能进行默认拷贝
*/在这里插入代码片

5.8 long long类型

/*

long long 类型实际上没有在C++ 98中存在,而之后被C99标准收录,其实现在市面上大多数编译器
是支持 long long 的,但是这个类型正式成为C++的标准类型是在C++11中。标准要求long long
至少是64位也就是8个字节。一个字面常量使用LL后缀表示long long类型,使用ULL后缀表示
unsigned long long 类型。
*/在这里插入代码片

5.9 列表初始化

//数组列表初始化
int xx[5]={1,2,3,4,5};
int yy[]={6,7,8,9,0};
 
//值类型进行初始化
int a{10};
int b={10};
int c={10.123}; // 编译器报错,g++ 5.3.1当列表初始化用于值类型的时候,如果有精度损失,编译器会报错。
 
//列表初始化还可以用结构体
typedef struct Str{
   int x;
   int y;
}Str;
Str s = {10,20};
 
//列表初始化类,必须是public成员,如果含有私有成员会失败
class Cls{
public:
   int x;
   int y;
};
Cls c  = {10,20};
 
//vector不仅可以使用列表初始化,还可以使用列表进行赋值,数组不能用列表赋值
vector<int>v1={1,2,3,4,5,6,7,8,9}; // 初始化
vector<int>v2;
v2={3,4,5,6,7}; //赋值
 
//map列表初始化
map<string ,int> m = {
      {"x",1},
      {"y",2},
      {"z",3}
};
 
//用函数返回初始化列表只展示关键代码,相关头文件自行添加
//同理结构体,类,map的返回也可以使用初始化列表返回
vector<int> getVector()
{
  return {1,2,3,4,5};
}
 
int main()
{
  vector<int> v = getVector();
  cout<<v[0]<<v[1]<<v.size()<<endl;
  return 0 ;
}在这里插入代码片

5.10 constexpr 变量

/*我们在定义常量的时候一般使用const来定义,一个常量必须在定义的时候进行初始化,
并且之后不可更改。一个常量必须使用一个常量表达式进行初始化,并且在编译期间就可以
得到常量的值,但是如何确定一个表达式就是常量表达式呢,这个通常是由程序员自己确定的,
例如:
*/


const int a =20;
//20是一个字面值,当然也是一个常量表达式,所以用20来为a赋值是没有问题的
//然而下面的代码也可以通过编译,g++ 5.3.1
int a = 20 ;
const int x =  a;
int b[x]={0};
/*为常量x赋值的是一个变量a,这样做应该是不合理的,但是编译器没有报告任何错误,
当然这种错误是显而易见的,但是在复杂的系统中如何判断一个表达式是否是常量表达式是很困难的,
例如这里的a我们一眼就可以判断其并不是一个常量表达式。为此C++11提供了一个新的关键字constexpr,
使用该关键字定义的常量,由编译器检查为其赋值的表达式是否是常量表达式,例如上面的代码改成:
*/
int a = 20 ;
constexpr int x =  a;
/*
编译器编译的时候就会报错说a并不是常量。显然constexpr关键字将常量表达式的检查转交给编译器处理,
而不是程序员自己,所以使用constexpr定义常量要比const安全。
*/在这里插入代码片

5.11 constexpr函数


//普通的函数一般是不能用来为constexpr常量赋值的,但是C++11允许定义一种constexpr的函数,
//这种函数在编译期间就可以计算出结果,这样的函数是可以用来为constexpr赋值的。
constexpr int size(int s)
{
    return s*4;
}

int a = 20;
const int b = 30;
constexpr int c = 40;
constexpr int si = size(a);  //error a是一个变量所以函数返回的是一个可变的值
constexpr int si1 = size(20); //ok 函数返回的实际上是一个常量
constexpr int si2 = size(b);  //ok
constexpr int si3 = size(c);  //ok在这里插入代码片

5.12 using类型别名

/*
类型别名其实早在C语言中就有了,一般情况下我们使用关键字typedef来声明一个类型的别名,
在C++11中增加了另一种声明类型别名的方法就是使用using关键字,using关键字在C++11以前一般用来引用命名空间。
*/

typedef int INT;  // 右侧符号代表左侧
using INT2 = int; // 左侧符号代表右侧
 
INT a = 20;
INT2 b = 30;在这里插入代码片
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-14 15:45:21  更:2021-12-14 15:47:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 12:26:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码