CPP高级语法(二)

158 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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用引用返回,要在模板中就表明这是个引用