.
- 学习网址
- 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 参数包
【解包】可变参数类型和数量的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), ...);
}