.

  • 学习网址
  • Person 类
  • 【C++】Copy control:移动构造器
  • 左值右值
  • 代码演示
  • 代码简化
  • 调用拷贝赋值进行拷贝构造
  • 调用移动赋值进行移动构造
  • copy&swap
  • 【initializer_list】初始化列表
  • tuple 使用继承解包
  • parameter package 参数包
  • 【解包】可变参数类型和数量的println
  • 【constexpr】写成一个
  • 【const 】和【constexpr】
  • const 常量
  • constexpr 编译时常量
  • 【 fold expression】c++17折叠
  • 改造println
  • 写一个sum
  • 函数模板还可以直接用于传值
  • c++中的逗号表达式的值
  • 整个表达式的值是最后一个值的
  • 【c++11:common_type_t】
  • common_type_t string, char = string
  • common_type_t int, double = double;
  • common_type_t A,B 自定义AB
  • 【common_type_t应用】 GetNth(获取参数包中的第n个参数)
  • 【C++20:lamda】可以加模板
  • mutable 可以修改捕获的变量,即使不是引用捕获
  • 也可以捕获引用去掉mutable

学习网址

https://www.bilibili.com/video/BV1wb4y1z7Aq?spm_id_from=333.999.0.0

代码均在vs2019-x64-debug模式下编译通过


Person 类

class Person {
public:
    string name;
    int age;
    Person() :name(""), age(0) {}

    Person(const char* _name, int _age) :name(_name), age(_age) {}
    Person(const Person& other) = default;
};

ostream& operator <<(ostream & os, Person const& per) {
    return os << "Person(" << per.name << ", " << per.age << ")";
}

【C++】Copy control:移动构造器

左值右值

左值:一个盒子,想要往里面装东西(地址空间)
右值:装在盒子里的内容(临时对象),“将亡值”,不代表内存空间,虽然占据了内存空间

代码演示

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>

using namespace std;

#define debug(x) do{cout<<#x<<": "<<(x)<<endl;}while(0);

class Buffer {
private:
    unsigned char* _buf;
    int _cap;
    int _len;
public:

    explicit Buffer(int cap) :_cap(cap), _len(0), _buf(new unsigned char [cap] {0}) {
        cout << "Construct" << endl;
    }
    
    Buffer(Buffer const& buffer) {

        cout << "Copy Construct" << buffer << endl;
        this->_cap = buffer._cap;
        this->_len = buffer._len;
        this->_buf = new unsigned char[buffer._cap];
        std::copy(buffer._buf, buffer._buf + buffer._cap, this->_buf);
    }

    Buffer& operator = (Buffer const& buffer) {

        cout << "Copy Assignment: " << buffer << endl;

        if (this != &buffer) {
            this->_cap = buffer._cap;
            this->_len = buffer._len;
            delete[] this->_buf;
            this->_buf = new unsigned char[buffer._cap];
            std::copy(buffer._buf, buffer._buf + buffer._cap, this->_buf);
        }
        return *this;
    }

    // 移动构造:buffer 右值
    Buffer(Buffer && buffer) noexcept {

        cout << "Move Construct: " << buffer << endl;
        this->_cap = buffer._cap;
        this->_len = buffer._len;
        this->_buf = buffer._buf;

        buffer._cap = 0;
        buffer._len = 0;
        // 赋值为空指针
        buffer._buf = nullptr;
    }

    // 移动赋值:buffer 右值
    Buffer& operator = (Buffer && buffer) noexcept {

        cout << "Move assignment: " << this->_cap << endl;
        // 判断自赋值
        if (this != &buffer) {

            this->_cap = buffer._cap;
            this->_len = buffer._len;

            delete[] this->_buf;
            this->_buf = buffer._buf;

            buffer._cap = 0;
            buffer._len = 0;
            // 赋值为空指针
            buffer._buf = nullptr;
        }
        return *this;
    }
    
    int GetLength() const {
        return _len;
    }

    int GetCapcity() const {
        return _cap;
    }

    ~Buffer() {
        cout << "Delte: " << *this << endl;
        delete[] _buf;
    }

    bool write(unsigned char value) {
        if (_len == _cap) return false;
        _buf[_len++] = value;
        return true;
    }

    friend ostream& operator << (ostream& os, const Buffer& buffer);

};

ostream& operator << (ostream& os, const Buffer  & buffer) {
    os << "Buffer(" << buffer.GetLength() << "/" << buffer._cap << ")[";
    for (int i = 0; i < buffer.GetCapcity(); ++i) {
        os << (int)buffer._buf[i] << ",";
    }
    os << "]";
    return os;
}

int main() {

    auto buffers = vector<Buffer>();
    cout << buffers.capacity() << endl;

    buffers.push_back(Buffer(12));
    buffers.push_back(Buffer(20));
    buffers.push_back(Buffer(30));
    buffers.push_back(Buffer(40));
    buffers.push_back(Buffer(50));

    return 0;
}

代码简化

调用拷贝赋值进行拷贝构造

调用移动赋值进行移动构造

需要首先进行初始化 _cap _len _buf
可以调用一个参数的构造函数进行初始化

class Buffer {
private:
    unsigned char* _buf;
    int _cap;
    int _len;
public:

    explicit Buffer(int cap) :_cap(cap), _len(0) {
        cout << "Construct" << endl;
        _buf  = (cap == 0 ? nullptr: new unsigned char [cap] {});
    }
    
    Buffer(Buffer const& buffer):Buffer(0) {

        cout << "Copy Construct" << buffer << endl;
        *this = buffer;
    }

    Buffer& operator = (Buffer const& buffer) {

        cout << "Copy Assignment: " << buffer << endl;

        if (this != &buffer) {
            this->_cap = buffer._cap;
            this->_len = buffer._len;
            delete[] this->_buf;
            this->_buf = new unsigned char[buffer._cap];
            std::copy(buffer._buf, buffer._buf + buffer._cap, this->_buf);
        }
        return *this;
    }

    // 移动构造:buffer 右值
    Buffer(Buffer && buffer) noexcept:Buffer(0) {

        cout << "Move Construct: " << buffer << endl;
        //*this = std::move(buffer);
        *this = static_cast<Buffer&&>(buffer);
        
    }

    // 移动赋值:buffer 右值
    Buffer& operator = (Buffer && buffer) noexcept {

        cout << "Move assignment: " << this->_cap << endl;
        // 判断自赋值
        if (this != &buffer) {

            this->_cap = buffer._cap;
            this->_len = buffer._len;

            delete[] this->_buf;
            this->_buf = buffer._buf;

            buffer._cap = 0;
            buffer._len = 0;
            // 赋值为空指针
            buffer._buf = nullptr;
        }
        return *this;
    }
    
    int GetLength() const {
        return _len;
    }

    int GetCapcity() const {
        return _cap;
    }

    ~Buffer() {
        cout << "Delte: " << *this << endl;
        delete[] _buf;
    }

    bool write(unsigned char value) {
        if (_len == _cap) return false;
        _buf[_len++] = value;
        return true;
    }

    friend ostream& operator << (ostream& os, const Buffer& buffer);

};

ostream& operator << (ostream& os, const Buffer  & buffer) {
    os << "Buffer(" << buffer.GetLength() << "/" << buffer._cap << ")[";
    for (int i = 0; i < buffer.GetCapcity(); ++i) {
        os << (int)buffer._buf[i] << ",";
    }
    os << "]";
    return os;
}

copy&swap

operator=的问题:
1 需要检查是不是自赋值
2 需要将自己原先内存释放之后新申请,这个时候如果申请抛出异常那么就程序就会出错
可以将operator=的参数去掉引用,直接先构造出来一个对象,这样就可以同时解决以上问题
还带来一个额外的好处,就是可以去掉operator=(&&)的版本:
<<<<当赋值运算符传入的参数是一个赋值的时候,参数就会按照右值的方式去构造
所有就相当于把传入的右值直接利用(“偷”,侯捷老师这么说)
然后执行的结果就和operator=(&&)一样了

class Buffer {
private:
    unsigned char* _buf;
    int _cap;
    int _len;
public:

    explicit Buffer(int cap) :_cap(cap), _len(0) {
        cout << "Construct" << endl;
        _buf = (cap == 0 ? nullptr : new unsigned char [cap] {});
    }

    static void Swap(Buffer& lhs,Buffer& rhs) noexcept{
        std::swap(lhs._buf,rhs._buf);
        std::swap(lhs._cap,rhs._cap);
        std::swap(lhs._len,rhs._len);
    }

    Buffer(Buffer & buffer) :
        _cap(buffer._cap),
        _len(buffer._len)
    {
        cout << "Copy Construct" << buffer << endl;
        _buf = (buffer._cap ? new unsigned char[buffer._cap]{} : nullptr);
        std::copy(buffer._buf, buffer._buf + buffer._cap, this->_buf);
    }

    Buffer& operator = (Buffer buffer) {

        cout << "Copy Assignment: " << buffer << endl;
        Swap(*this, buffer);
        return *this;
    }

    // 移动构造:buffer 右值
    Buffer(Buffer&& buffer) noexcept :Buffer(0) {
        cout << "Move Construct: " << buffer << endl;
        Swap(*this,buffer);
    }

    // 移动赋值:buffer 右值 no need 
    
    int GetLength() const {
        return _len;
    }

    int GetCapcity() const {
        return _cap;
    }

    ~Buffer() {
        cout << "Delte: " << *this << endl;
        delete[] _buf;
    }

    bool write(unsigned char value) {
        if (_len == _cap) return false;
        _buf[_len++] = value;
        return true;
    }

    friend ostream& operator << (ostream& os, const Buffer& buffer);

};

ostream& operator << (ostream& os, const Buffer& buffer) {
    os << "Buffer(" << buffer.GetLength() << "/" << buffer._cap << ")[";
    for (int i = 0; i < buffer.GetCapcity(); ++i) {
        os << (int)buffer._buf[i] << ",";
    }
    os << "]";
    return os;
}

【initializer_list】初始化列表

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
#include <initializer_list>

using namespace std;

#define debug(x) do{cout<<#x<<": "<<(x)<<endl;}while(0);

class Person {
public:
    string name;
    int age;
    Person() :name(""), age(0) {}

    Person(const char* _name, int _age) :name(_name), age(_age) {}
    Person(const Person& other) = default;
};

ostream& operator <<(ostream & os, Person const& per) {
    return os << "Person(" << per.name << ", " << per.age << ")";
}

template<typename T>
void println(initializer_list<T> args) {
    for (auto& i : args) {
        cout << i << ",";
    }
    cout << endl;
}

class PrintlnClass {
public:
    template<typename T>
    PrintlnClass(initializer_list<T> args) {
        for (auto& i : args) {
            cout << i << ",";
        }
        cout << endl;
    }
};

int main() {

    //Error使用{}不可以类型收窄
    //auto p1 = Person{ "abcde",12.0 };
    auto p1 = Person("abcde",12.0);
    
    auto p2 = Person{ "abcde",12 };
    Person s{ p2 }; //拷贝构造

    println({1,2,3,4});
    PrintlnClass{5,4,3,2,1};

    cout <<"sizeof(PrintlnClass): "<< sizeof(PrintlnClass) << endl;

    return 0;
}

tuple 使用继承解包

parameter package 参数包

【C++魔法:bennyhuo&持续更新】initializer_list、constexpr、 fold expression折叠_赋值

【解包】可变参数类型和数量的println

void print() {}
template<typename T, typename ... Types>
void print(const T& firstArg, const Types&... args){
    
    cout << "size of args: " << sizeof...(args) << endl;
    cout << firstArg << endl;
    print(args...);
}

【constexpr】写成一个

template<typename T, typename ...U>
void println(T v, U ...u) {
	if constexpr (sizeof...(U) == 0) {
		std::cout << v << std::endl;
	}
	else {
		std::cout << v << ", ";
		println(u...);
	}
}

【const 】和【constexpr】

const 常量

constexpr 编译时常量

可以作为数组初始化的大小值
constexpr 可以修饰函数的返回值是编译期的常量
使用函数的返回值初始化数组大小

constexpr int GetLen() {
	return 9;
}

int GetLen1() {
	return 9;
}

int main() {

	const int n1 = 5;
	int a[n1];
	constexpr int n2 = 10;
	int b[n2];
	int c[GetLen()]; //正确
	//int d[GetLen1()];//错误

    return 0;
}

【 fold expression】c++17折叠

改造println

#include <iostream>
#include <string>

using namespace std;

template<typename... U>
void println(U ...u) {
	( (cout << u << ","), ...)<<endl;
}

int main() {
	println("abc", 1, 4.5);
    return 0;
}

写一个sum

#include <iostream>
#include <string>

using namespace std;

template<typename... U>
double Sum(U ...u) {
	//return (u + ...);
	return (... + u);
}

int main() {

	cout << Sum(1, 2, 4.5) << endl;
	return 0;
}

函数模板还可以直接用于传值

#include <iostream>
#include <string>

using namespace std;

template<auto ... u>
double Sum() {
	return (u + ...);
}

int main() {
	cout << Sum<1, 2, 3,4>() << endl;
    return 0;
}

c++中的逗号表达式的值

整个表达式的值是最后一个值的

#include <iostream>
using namespace std;

int main() {

	cout << (0) << endl;
	cout << (0, 1) << endl;
	cout << (0, 1, 2) << endl;
	//0
	//1
	//2
	return 0;
}

【c++11:common_type_t】

common_type_t string, char = string

#include <iostream>
#include <string>

using namespace std;

int main() {

	using Comm1 = common_type_t<string, char*>;
	using Comm2 = common_type_t<string, char*>;
	Comm1 a = "abc";
	Comm2 b = "abcd";
	cout << a << endl;
	cout << typeid(a).name() << endl;
	cout << b << endl;
	cout << typeid(b).name() << endl;
	//abc
	//class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> >
	//abcd
	//class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> >
	return 0;
}

common_type_t int, double = double;

#include <iostream>
#include <string>

using namespace std;

int main() {

	using Comm = common_type_t<int, double>;
	Comm a = 7;
	cout << a << endl;
	cout << typeid(a).name() << endl;
	//7
	//double
	return 0;
}

common_type_t A,B 自定义AB

#include <iostream>
#include <string>

using namespace std;

class A {
	A() {}
};

class B {
public:
	B() {}
	B(A a) {}
};

int main() {

	using Comm1 = common_type_t<B, A>;
	using Comm2 = common_type_t<A, B>;

	Comm1 a;
	Comm2 b;
	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;

	//[output]
	//class B
	//class B
	return 0;
}

发生了隐式转换
与视频中的不一样
这里的可以AB互换位置

class B {
public:
	B() {}
	explicit B(A a) {}
};

同样的
加上explicit之后就不可以了,编译会报错


【common_type_t应用】 GetNth(获取参数包中的第n个参数)

#include <iostream>
#include <string>

using namespace std;

template<typename... U>
auto GetNth(int n ,U ...u) {

	using commonT = common_type_t<U...>;
	commonT ret;

	int i = 0;
	((i++ == n ? (ret = u,true):false)|| ...);
	return ret;
}

int main() {

	cout << GetNth(0, 2, 4, 3.1f) << endl;
	cout << GetNth(1, 2, 4, 3.1f) << endl;
	cout << GetNth(2, 2, 4, 3.1f) << endl;
	//2
	//4
	//3.1
	return 0;
}

【C++20:lamda】可以加模板

mutable 可以修改捕获的变量,即使不是引用捕获

#include <iostream>
#include <string>

using namespace std;

template<typename... U>
void printN(U ...u) {

	int i = 0;
	auto f = [i] <typename Arg> (Arg arg) mutable {
		if (i++ == sizeof...(u) - 1) {
			cout << arg << endl;
		}
		else {
			cout << arg << ",";
		}
	};

	(f(u), ...);
}

int main() {

	printN(0, 2, 4, 3.1f);
	printN(1, 2, 4, 3.1f);
	//0, 2, 4, 3.1
	//1, 2, 4, 3.1
	return 0;
}

也可以捕获引用去掉mutable

template<typename... U>
void printN(U ...u) {

	int i = 0;
	auto f = [&i] <typename Arg> (Arg arg) {
		if (i++ == sizeof...(u) - 1) {
			cout << arg << endl;
		}
		else {
			cout << arg << ",";
		}
	};

	(f(u), ...);
}