C++11现代特性

现代C++核心语言特性

先速通了一波环境,注意这里每次xlings install之后一定要记得 xlings update一下,要不然会报错,包括d2x的version 也要在 0.1.5以上啊

auto 和 decltype()的使用

习题一

修改过后的代码部分,不得不说 auto和decltype()确实好用,就我感觉而言,auto的话就不需要管后面那些是什么(不管是变量还是表达式,编译器自己会作出决断),但是你的 decltype()括号里需要看后面表达式或者是变量是什么,但也是编译器去决断不需要人眼观察

#include <d2x/cpp/common.hpp>

int main() {

    // 0. Declaration and definition
    int a = 1;
    auto a1 = a; // a1's type is int
    int b = 2;
    auto b1 = b;

    decltype(b) b2 = b; // b2's type is int
    auto a2 = a;

    char c = 'c';
    auto c1 = c;
    auto c2 = c;

    d2x_assert_eq(a, a1);
    d2x_assert_eq(a1, a2);
    d2x_assert_eq(b, b1);
    d2x_assert_eq(b1, b2);
    d2x_assert_eq(c, c1);
    d2x_assert_eq(c1, c2);

    //D2X_WAIT

    return 0;
}

习题二

#include <d2x/cpp/common.hpp>

int main() {

    // 1. Expressions
    int a = 1;
    auto a1 = a + 2;
    auto a2 = a + 2 + 1.1;

    int b = 2;
    decltype(a+0.1) b1 = a + 0.1;
    decltype(a + b + 1.1) b2 = a + b + 1.1;

    char c = 'c';
    auto c1 = 1 + c;
    auto c2 = 2 + 'a';

    d2x_assert_eq(a2, a + 2 + 1.1);
    d2x_assert_eq(b1, a + 0.1);
    d2x_assert_eq(c1, 1 + c);
    d2x_assert_eq(c2, 2 + 'a');

    //D2X_WAIT

    return 0;
}

习题三

#include <d2x/cpp/common.hpp>

#include <iostream>
#include <vector>
#include <functional>

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

int main() {

    // 2. Complex types

    std::vector<int> v = {1, 2, 3};

    std::vector<int>::iterator v1 = v.begin();
    for (; v1 != v.end(); ++v1) {
        std::cout << *v1 << " ";
    }
    std::cout << std::endl;

    auto v2 = v.begin();
    for (; v2 != v.end(); ++v2) {
        std::cout << *v2 << " ";
    }
    std::cout << std::endl;

    auto minus_func = [](int a, int b) { return a - b; };

    std::vector<std::function<decltype(add_func)>> funcVec = {
        add_func,
        minus_func
    };

    d2x_assert_eq(funcVec[0](1, 2), 3);
    d2x_assert_eq(funcVec[1](1, 2), -1);

    //D2X_WAIT

    return 0;
}

习题四

#include <d2x/cpp/common.hpp>

#include <iostream>
#include <vector>

// 3. Function return types

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

template<typename T1, typename T2>
auto minus_func(T1 a, T2 b) -> decltype(a - b) {
    return a - b;
}
//按照前面 add_func的操作将 minus_func()照做就OK啦!
int main() {

    d2x_assert_eq(minus_func(1, 2), -1);
    d2x_assert_eq(minus_func(2, 1), 1);
    d2x_assert_eq(minus_func(1, 2.1), -1.1);

    //D2X_WAIT

    return 0;
}

习题五

 **提示时间!**
   - `(obj.a)` 是带括号的表达式 → `const int &`
   - `obj.b` 直接取成员 → `double`  
   - `(obj.b)` 带括号表达式 → `double &`
   
   
   我的理解:obj.b:这是成员访问表达式,直接取结构体/类的成员。(obj.b):这是括号表达式,括号内是成员访问表达式,整体作为一个左值表达式。
   
   补了一发左值(指向特定内存具有名称的值)和右值(指临时短暂的表达式或值)的概念
   下面代码为什么obj.a 直接用他成员类型即可,而obj.b 直接用double 不行?
   因为**const Object obj** 这个对象 obj是 const 修饰了,那么整个成员变量都需要加上 const才起作用 
#include <d2x/cpp/common.hpp>

#include <type_traits>


// 4. Class/Struct member type deduction

struct Object {
    const int a;
    double b;
    Object() : a(1), b(2.0) { }
};

int main() 
{
    const Object obj;

    bool type_check = false;

    // Type deduction for obj and (obj)
    type_check = std::is_same<decltype(obj), const Object>::value;
    d2x_assert(type_check); type_check = false; // dont change this line
    type_check = std::is_same<decltype((obj)), const Object &>::value;
    d2x_assert(type_check); type_check = false; // dont change this line

    // Type deduction for obj.a and (obj.a)
    type_check = std::is_same<decltype(obj.a), const int>::value;
    d2x_assert(type_check); type_check = false; // dont change this line
    type_check = std::is_same<decltype((obj.a)), const int &>::value;
    d2x_assert(type_check); type_check = false; // dont change this line

    // Type deduction for obj.b and (obj.b)
    type_check = std::is_same<decltype(obj.b), double >::value;
    d2x_assert(type_check); type_check = false; // dont change this line
    type_check = std::is_same<decltype((obj.b)), const double & >::value;
    d2x_assert(type_check); type_check = false; // dont change this line

    //D2X_WAIT

    return 0;
}

习题六

需要注意:auto 会剥离顶层 const 和引用
也就是说我们使用 auto的时候 ,后面不管是左值或者右值,后面填写的都只是类型名

#include <d2x/cpp/common.hpp>
#include <type_traits>
// 5. const & reference stripping and preservation

int main() {
    const int ci = 1;
    int n = 2;
    int& ri = n;

    bool type_check = false;

    // auto strips top-level const: what does a deduce to?
    auto a = ci;
    type_check = std::is_same<decltype(a), const int >::value;
    d2x_assert(type_check); type_check = false; // dont change this line

    // auto strips the reference: what does b deduce to? (b is an independent copy of n)
    auto b = ri;
    type_check = std::is_same<decltype(b), const int>::value;
    d2x_assert(type_check); type_check = false; // dont change this line

    // To keep a const reference const int&: how should cr be declared?
    auto cr = ci;
    type_check = std::is_same<decltype(cr), const int >::value;
    d2x_assert(type_check); type_check = false; // dont change this line

    // To preserve the declared type const int exactly: how should d be declared?
    decltype(ci) d = ci;
    type_check = std::is_same<decltype(d), const int &>::value;
    d2x_assert(type_check); type_check = false; // dont change this line
    //D2X_WAIT
    return 0;
}

default_and_delete

补了一发 default 和 delete用法,还是听容易理解的,两者都作用于构造函数那一类,default 就相当于我就是默认用系统提供给我的,而delete 相当于我让其他程序员知道 我不用系提供给我的那一套

习题一

#include <d2x/cpp/common.hpp>

#include <iostream>

// default and delete explicitly control -> compiler default constructor generation behavior
struct A { 
};
struct B {
    B() = default;
    B(int x) { std::cout << "B(int x)" << std::endl; }
};
struct C {
    C()=delete;
    C(int x = 1) { std::cout << "C(int x = 1)" << std::endl; }
};

int main() { // Do not directly modify the code in the main function

    A a;
    B b;
    C c(1);

    //D2X_WAIT

    return 0;
}

习题二

拷贝构造函数就是一个对去初始化另外一个对象
class Num
{};
Num(const Num &another)
{}
#include <d2x/cpp/common.hpp>

#include <iostream>

// Implement std::unique_ptr property: not copyable but movable
struct UniquePtr {
    void *dataPtr;
    UniquePtr() = default;
    UniquePtr(const UniquePtr&) = delete; 
    UniquePtr & operator = (const UniquePtr &) = delete;
    UniquePtr(UniquePtr &&) = default;
    UniquePtr & operator = (UniquePtr &&) = default;
};

int main() { // Do not directly modify the code in the main function

    // std::unique_ptr<int> a(new int(1));
    UniquePtr a;

    // Object cannot be copied/duplicated
    // std::unique_ptr<int> b = a; // error
    d2x_assert(std::is_copy_constructible<UniquePtr>::value == false);
    // a = b; // error
    d2x_assert(std::is_copy_assignable<UniquePtr>::value == false);

    // Object can be moved
    // std::unique_ptr<int> c = std::move(a); // ok
    d2x_assert(std::is_move_constructible<UniquePtr>::value == true);
    // a = std::move(c); // ok
    d2x_assert(std::is_move_assignable<UniquePtr>::value == true);

    //D2X_WAIT
    return 0;
}

习题三

#include <d2x/cpp/common.hpp>
#include <iostream>

void func(int x) {
    std::cout << "x = " << x << std::endl;
}

// Explicitly delete float parameter overload
//void func(float) = delete;


int main() 
{
    func(1);     // int
    //func(int(1.1);  // float
    //上述是丢失精度的做法,如果不丢失精度,就得把上面 void func(float) = delete删除
	float(1.1);
    //D2X_WAIT

    return 0;
}

final_and_override

override针对于子类继承父类后,父类中用 ```virture```修饰过的,子类可以重写,但是必须是父类中的方法
主要还是为了 防止我们写错相应方法做的补充

final 和 override 都是实现多态的方式,final 的意思是 不允许父类的虚函数再被**其他有虚函数的子类重写**
#include <d2x/cpp/common.hpp>

#include <iostream>
#include <string>

struct A {
    virtual void func1() {
        std::cout << "A::func1()" << std::endl;
    }

    void func2() {
        std::cout << "A::func2()" << std::endl;
    }
};

struct B : A {
    void func1()  {
        std::cout << "B::func1()" << std::endl;
    }

    void func2() {
        std::cout << "B::func2()" << std::endl;
    }
};


int main() {

    B override; // Do not directly modify the code in the main function
    override.func1(); // B::func1()
    override.func2(); // B::func2()

    A *a = &override;
    a->func1(); // B::func1()
    a->func2(); // A::func2()

    //D2X_WAIT

    return 0;
}

习题二

这题为什么```A *a = &final ```的时候 ```a->fun1()```是 3 呢,而后面 ```a->func2() ```却是2?
我猜测 因为func1()是虚函数,下面又有子类进行重写(override) 那么就是默认调用为子类的方法
而 fun2()并没有在父类中虚函数定义,自然而然调用的还是 A的方法 (即使指向了B这个对象)
#include <d2x/cpp/common.hpp>
#include <iostream>
#include <string>
struct A {
    virtual int func1() {
       return 1;
    }
    int func2() 
    {
         return 2; 
    }
};

struct B : A  
{
    int func1()
	{
        return 3;
		}

    int func2() 
    {
        return 4;
    }
};

int main() 
{

    B final; // Do not directly modify the code in the main function
    d2x_assert_eq(final.func1(), 3); // B::func1()
    d2x_assert_eq(final.func2(), 4); // B::func2()

    A *a = &final;
    d2x_assert_eq(a->func1(), 3); // B::func1()
    d2x_assert_eq(a->func2(), 2); // A::func2()
    //D2X_WAIT
    return 0;
}

习题三

父类是 AudioPlayer 子类是WAVPlayer、MP3Player、OGGPlayer 但是请注意 父类i的play()是final关键字修饰,说明子类不可重写play()方法 但是 init_audio_params() 和 play_audio() 方法是可重写的,于是我们直接重写这两个方法即可
#include <d2x/cpp/common.hpp>

#include <iostream>
#include <string>

struct AudioPlayer { // Do not directly modify the AudioPlayer class
    virtual void play() final{
        init_audio_params();
        play_audio();
    }
private:
    virtual void init_audio_params() = 0;
    virtual void play_audio() = 0;
};

struct WAVPlayer : AudioPlayer {
    void init_audio_params() override {
        std::cout << "WAVPlayer: Initializing audio parameters..." << std::endl;
    }

    void play_audio() override {
        std::cout << "WAVPlayer: Playing WAV audio..." << std::endl;
    }
};

struct  MP3Player : AudioPlayer {
    void init_audio_params() override {
        std::cout << "MP3Player: Initializing audio parameters..." << std::endl;
    }

    void play_audio() override {
        std::cout << "MP3Player: Playing MP3 audio..." << std::endl;
    }
};

struct OGGPlayer : AudioPlayer {
    // Correctly implement OGGPlayer


        // init_audio_params();
        void init_audio_params()override
        {
            std::cout << "OGGPlayer: Initializing audio parameters..." << std::endl;
        }
        
        void play_audio() override {
        // play_audio();
        std::cout << "OGGPlayer: Playing OGG audio..." << std::endl;
        }
};


int main() { // Do not directly modify the code in the main function

    AudioPlayer *player1 = new WAVPlayer();
    AudioPlayer *player2 = new MP3Player();
    AudioPlayer *player3 = new OGGPlayer();

    player1->play();
    player2->play();
    player3->play();

    delete player1;
    delete player2;
    delete player3;

    //D2X_WAIT

    return 0;
}

Trailing_and_return type

我理解的 return_type 这部分内容指的是 编译器自动帮你解析出数据类型 
比如 double + double 的数据类型,如果想要最后是 int 的结果,那么就在后面加一个 ->int{}即可

习题一

#include <d2x/cpp/common.hpp>

#include <iostream>

int add0(double a, int b) {
    return a + b;
}

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

template<typename T1, typename T2>
auto add2(const T1 &a, const T2 &b) -> decltype(a+b)
{
    return a + b;
}

auto add3 = [](double a, double b) -> int {
    return a + b;
};

int main() {

    d2x_assert_eq(add0(1.1, 2), 3);
    d2x_assert_eq(add1(1.1, 2), 3);
    d2x_assert_eq(add2(1.1, 2), 3.1);
    d2x_assert_eq(add2(1, 2.1), 3.1);
    d2x_assert_eq(add3(1.1, 2.1), 3);

    //D2X_WAIT

    return 0;
}

rvalue references

这里讨论的是如何用右值引用延长对象的生命周期,比对了绑定到左值引用上不能修改这个临时对象, 而使用Object&& 用右值引用后面依然可以修改这个对象中的内容
在下述代码中,因为 `const Object &` 就像给临时对象贴了"请勿触摸"的标签,所以
这是左值引用延长临时对象生命周期的办法:在最左边加一个 const 
const Object &objRef = Object(); 
而右值引用 删除const 再在后面加一个 & 即可: 
Object &&objRef = Object(); 

习题一

#include <d2x/cpp/common.hpp>

#include <iostream>
#include <string>

struct Object;
static Object * object_address = nullptr;

struct Object {
    int data = 0;
    Object() {
        std::cout << "Object():" << this << std::endl;
        object_address = this;
    }
    Object(const Object&) { std::cout << "Object(const Object&):" << this << std::endl; }
    Object(Object&&) { std::cout << "Object(Object&&):" << this << std::endl; }
    ~Object() { std::cout << "~Object():" << this << std::endl; }
};

int main() { // Disable compiler optimization
    {
        std::cout << "----> Temporary object - rvalue 1" << std::endl;
        Object();
        std::cout << "----> Temporary object - rvalue 2" << std::endl;
        Object obj = Object();

        std::cout << "--------Code modifiable area - Start--------" << std::endl;


        Object &&objRef = Object(); // Extend temporary object lifetime


        std::cout << "--------Code modifiable area - End--------" << std::endl;

        objRef.data = 1; // Modify the value of the extended lifetime temporary object (do not directly modify this line)
        std::cout << "objRef.data = " << objRef.data << " - " << &objRef << std::endl;
        d2x_assert((&objRef == object_address));
    }

    //D2X_WAIT

    return 0;
}

move-semantics

移动语义在上述右值引用的基础下, 通过一个构造函数创建另一个对象,让原来对象的指针失效,实现了一个转移的效果
不需要把数据一个个复制i过去
步骤就分三步:
1.释放旧资源
2.转移该对象资源
3.让该对象指针指空

习题一

编译器告诉我们 **buff2DataPtr == buff3DataPtr**  不对等,为什么呢?
是因为 **other.data = nullptr;** 我们已经旧的对象的指针指空了,相当于他里面无数据了,所以不能直接采用赋值的方式拷贝数据,而是应该用 **std::move** 实现数据移动的操作
#include <d2x/cpp/common.hpp>
#include <iostream>

struct Buffer {
    int *data;
    Buffer() : data { new int[2] {0, 1} } {
        std::cout << "Buffer():" << data << std::endl;
    }
    Buffer(const Buffer &other)  {
        std::cout << "Buffer(const Buffer&):" << data << std::endl;
        data = new int[2];
        data[0] = other.data[0];
        data[1] = other.data[1];
    }
    Buffer(Buffer&& other) : data { other.data } { 
        std::cout << "Buffer(Buffer&&):" << data << std::endl;
        other.data = nullptr; // Invalidate the pointer of the original object
    }
    ~Buffer() {
        if (data) {
            std::cout << "~Buffer():" << data << std::endl;
            delete[] data;
        }
    }
    const int * data_ptr() const { return data; }
};

Buffer process(Buffer buff) {
    std::cout << "process(): " << buff.data << std::endl;
    return buff;
}

int main() {
    {
        Buffer buff1 = process(Buffer());
        auto buff1DataPtr = buff1.data_ptr();

        std::cout << " --- " << std::endl;

        Buffer buff2(std::move(buff1));
        auto buff2DataPtr = buff2.data_ptr();

        d2x_assert(buff1DataPtr == buff2DataPtr);

        Buffer buff3 = std::move(buff2);
        auto buff3DataPtr = buff3.data_ptr();

        d2x_assert(buff2DataPtr == buff3DataPtr);

        Buffer buff4 = std::move(buff3);
        auto buff4DataPtr = buff4.data_ptr();

        d2x_assert(buff3DataPtr == buff4DataPtr);
    }

    //D2X_WAIT

    return 0;
}

习题二

#include <d2x/cpp/common.hpp>
#include <iostream>
static int move_assignment_counter = 0;
struct Buffer {
    int *data;
    Buffer() : data { new int[2] {0, 1} } {
        std::cout << "Buffer():" << data << std::endl;
    }
    Buffer(const Buffer &other) {
        std::cout << "Buffer(const Buffer&):" << data << std::endl;
        data = new int[2];
        data[0] = other.data[0];
        data[1] = other.data[1];
    }
    Buffer(Buffer&& other) : data { other.data } {
        std::cout << "Buffer(Buffer&&):" << data << std::endl;
        other.data = nullptr; // Invalidate the pointer of the original object
    }
    Buffer & operator=(const Buffer &other) {
        std::cout << "Buffer& operator=(const Buffer&):" << data << std::endl;
        if (this != &other) {
            delete[] data; // Release old resources
            data = new int[2];
            data[0] = other.data[0];
            data[1] = other.data[1];
        }
        return *this;
    }
    Buffer & operator=(Buffer&& other) {
        move_assignment_counter++;
        std::cout << "Buffer& operator=(Buffer&&):" << data << std::endl;
        if (this != &other) {
            delete[] data; // Release old resources
            data = other.data; // Transfer resources
            other.data = nullptr; // Invalidate the pointer of the original object
        }
        return *this;
    }
    ~Buffer() {
        if (data) {
            std::cout << "~Buffer():" << data << std::endl;
            delete[] data;
        }
    }
    const int * data_ptr() const {
        std::cout << "data[0] = " << data[0] << ", data[1] = " << data[1] << std::endl;
        return data;
    }
};

Buffer process(Buffer buff) {
    std::cout << "process(): " << buff.data << std::endl;
    return buff;
}

int main() { // No compiler optimization
 
    {
        Buffer buff1;

        buff1 = Buffer(); // Case 1: Temporary object assignment

        d2x_assert_eq(move_assignment_counter, 1);

        Buffer buff2;

        buff2 = process(buff1); // Case 2: Intermediate object assignment

        d2x_assert_eq(move_assignment_counter, 2);

        buff2 = std::move(buff1); // Case 3: Explicit move assignment

        d2x_assert_eq(move_assignment_counter, 3);

    }

    //D2X_WAIT

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值