开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情
三、Copy constructer
#include<iostream>
class String {
private:
char* m_Buffer;
int m_Size;
public:
String(const char* str) {
m_Size = strlen(str);
m_Buffer = new char[m_Size + 1];//+1是为了存放截止符0
memcpy(m_Buffer, str, m_Size + 1);
m_Buffer[m_Size] = 0;
}
//copy constructer ,cpp默认会创建一个copy constructer ,当你运行String str2 = str1 时会自动调用,
//但默认的copy constructer只是浅复制,即只复制指针,不复制指针指向内存的值,如下所示
String(const String& other) {
memcpy(this, &other, sizeof(String));
}
//cpp默认copy constructer,和上面写法等价。可见m_Buffer指针和other.m_Buffer指向的同一片地址
String(const String& other)
:m_Buffer(other.m_Buffer),m_Size(other.m_Size)
{
}
//深复制
String(const String& other)
:m_Size(other.m_Size) {
m_Buffer = new char[m_Size + 1];
memcpy(m_Buffer, other.m_Buffer, m_Size + 1);
//这里不用m_Buffer[m_Size] = 0;因为other 中的m_Buffer已经又了0截止符
}
String(const String& other) = delete //禁用copy constructer,这样String str2 = str1就会报错
~String() {
delete[] m_Buffer;
}
//friend关键字,使函数可以调用被private修饰的成员变量
friend std::ostream& operator<<(std::ostream& stream, const String& str);
char& operator[](unsigned int index)const {
return m_Buffer[index];
}
};
std::ostream& operator<<(std::ostream& stream, const String& str) {
stream << str.m_Buffer;
return stream;
}
int main(int argc,char** argv){
String str = String("Dragon");
String str2 = str;
str2[2] = 'e';
std::cout << str << std::endl;
std::cout << str2 << std::endl;
}
四、CPP标准模板库(std)/容器
标准模板库本质上是一个装满容器的库。
为什么不用using namespace std;,因为用了它就很难分辨是否是标准库的函数。如果实在需要使用就把using namespace std放在足够小的作用域里,千万别放在头文件中。
(简单介绍一下namespace(命名空间))为避免命名冲突,最好将自己写的代码库和项目放在namespace里面。
namespace yzr{
void print(){};
}
int main(){
//调用yzr命名空间中的print函数。和调用静态函数,类方法的方法一样,因为本质上class也是命名空间
yzr::print();
}
1、Vector(动态数组)
#include <iostream>
#include <vector>
class Vertex {
public:
int x, y, z;
};
std::ostream& operator<<(std::ostream& stream, const Vertex& v) {
stream << v.x << "," << v.y << "," << v.z;
return stream;
}
//记住如果要将vector传给函数记住要加&(reference),这样就不会把vector复制一遍
void Function(const std::vector<Vertex>& vertices){}
int main(){
std::vector<Vertex> vertices;
vertices.push_back(Vector(1,2,3));//添加元素
vertices.push_back({ 4,5,6 });
for (int i = 0; i < vertices.size(); i++) {
std::cout << vertices[i] << std::endl; // 之所以能vertices[i]是因为vertor重载了[]运算符
}
//vertices.clear();//清空vector
vertices.erase(vertices.begin() + 1);//清空vector的第二个元素,vertice.begin()获得的是第一个元素
for (Vertex& v : vertices) {
std::cout << v << std::endl;
}
std::cin.get();
}
0x1 vector 优化使用
当我们创建一个vector 并通过push_back向vector中添加元素时,如果vector空间不够,cpp会开辟一片足够的空间,并将当前vector中的内容从旧位置复制到新位置,随后删除旧空间。这个过程会消耗cpu资源,因此也是优化的重点。
Vertex(const Vertex& other){ // copy constructer
this = &other;
std::cout << "copied"<<std::endl;
}
vertices.reserve(3); //一开始就将vecctor的capacity设置为2(预期大小)
//vertices.push_back(Vertex(1,2,3));//这种写法会先床罩vertex实例然后复制给vertices。造成一次copy
vertices.emplace_back(1, 2, 3);//用emplace_back替代push_back 就不会在main函数中创建Vertex实例,那么就不需要复制了
0x2 排序
#include<algorithem>
#include<vector>
#include<functional>
int main(){
std::vector v = {1,2,3,4};
std::sort(v.begin(),v.end(),std::greater<int>());
std::sort(v.begin(),v.end(),[](int a, int b){
return a>b // 当返回为true时,a放在b前面,当返回为false时b放在a前面。
});
}
2、Array(静态数组)
array不可改变大小
#include<array>
#include<string>
int main(){
std::array<std::string,2> Array; // 创建长度为2的string类型数组
}
array会在栈上创建而vector在堆上创建,因此array更快
3、Tuple、pair、std::tie(元组)
#include<tuple>
#include<iostream>
#include<string>
std::tuple<std::string, int>getPersonInfo() {
std::string name = "yzr";
int age = 22;
return std::make_pair(name, age);//也可以{name,age},以及std::make_tupe(name,age)
}
int main(int argc,char** argv){
std::tuple<std::string, int> p = getPersonInfo(); // 两种获取tuple返回值的方式
auto p2 = getPersonInfo();// 两种获取tuple返回值的方式
std::string name = std::get<0>(p);//std::get<0>(p)这种方式获取person的name就非常不直观了
int age = std::get<1>(p2);
std::cout << name << std::endl;
std::cout << age << std::endl;
}
std::pair<std::string, int>getPersonInfo() { //如果返回的值只有两个就可以用pair
std::string name = "yzr";
int age = 22;
return std::make_pair(name, age);
}
int main(){
std::tuple<std::string, int> p = getPersonInfo();
auto p2 = getPersonInfo();//当类型特别长的时候可以用auto简化代码,auto会自动识别类型,但是auto不处理引用,因此要const auto& a=***;
std::string name = std::get<0>(p);
int age = p2.second;//相较于tuple多了 first,second 的取值方法
std::string name;
int age;
std::tie(name, age) = getPersonInfo();//用std::tie也可以
auto[name,age] = getPersonInfo();//cpp 17+的新特性,结构化绑定
std::cout << name << std::endl;
std::cout << age << std::endl;
}
4、Optional (cpp 14特性)
#include
std::optional function(param){statement; return type;}
auto result = function();
1: result.has_value()判断数据是否存在, 通过result.value()获取数据
2: result.value_or(xxx)其中xxx作为默认值,如果存在数据返回数据,不存在返回xxx
3:通过if (result)判断数据是否存在
注: 使用场景—目标值可能存在也可能不存在,比如读取文件并返回内容,可能读取成功有数据,读取成功无数据,读取不成功。
#include<iostream>
#include<optional>
#include<fstream>
#include<string>
#include<vector>
std::optional<std::string> readFileAsString(const std::string& filepath) {
std::ifstream stream = std::ifstream(filepath);
if (stream) {
std::string line;
std::vector<std::string> result;
while(std::getline(stream,line)){
result.push_back(line);
}
stream.close();
return result;
}
else {
return {};
}
}
int main(){
std::optional<std::string> data = readFileAsString("data.txt");
data.value_or("not present");//没有值时输出not present
if (data) {//data.has_value()
std::cout << "File read success" << std::endl;
std::cout << data<< std::endl;
}
else {
std::cout << "File read failed" << std::endl;
}
}
5、Variant(cpp 17特性)
作用:如果希望在一个变量中存储多个不同类型的数据用variant
#include <variant>
std::variant<type1, type2> data; //类似于union,type1与type2表示存储的数据类型。
data = type1(xxx)
读取:
1: std::get(data)
2: auto value = std::get_if(&data)
注:类型安全
eg:
#include<string>
#include<iostream>
#include<variant>
int main(int argc,char** argv){
std::variant<std:string,int>value1 = "Dragon";
std::variant<std:string,int>value2 = 32;
if(std::get_if<std::string>(&value1)){ //是std::stting的话返回指向那个string的指针,反之返回空指针
//处理string
std::string value = *value;
}else{
//处理int
}
}
6、Any(cpp17 特性)
和std::variant区别:variant要求列出我们希望variant保存数值的所有类型,而any不需要我们去关心类型,总的来说variant更加类型安全,在处理较大数据时速度更快。any在存储超过32字节的数据时会进行动态内存分配
作用:基本没啥用,如果想要在一个变量中存储不同类型的数据可以用结构体或者tuple或者variant,如果想要一个指向不明类型的buffer(缓冲区)指针,完全可以用void*
#include<any>
#include<iostream>
int main(int argc,char** argv){
std::any value;
value = 1;
value = std::string("dragon");
std::string& res = std::any_cast<std::string&>(value);//如果想要any_cast用引用返回,要在模板中就表明这是个引用