在学习这节课之前,复习了一下之前的代码:
class Entity{
public:
int x, y;
Entity(int x,int y): x(x),y(y) { std::cout << "created" << std::endl; }
};
Entity e(1,1);
Entity* e1 = &e;
Entity& e2 = *e1;
Entity e3(3, 3);
e2 = e3;
e2.x = 2;
std::cout << e3.x << std::endl;
std::cout << e.x << std::endl;
输出3,2
e2引用后e就不能改变对象,哪怕不会报错,但对e2操作还是会操作在e上,而不是e3。使用引用来避免数据复制,参数传递、返回值、运算符重载等等都会用到。
箭头操作符
一般在指针对对象访问的情况使用,有时还会用到操作符重载,如下:
class Entity{
public:
int x, y;
void Print() const {
std::cout << "hello!" << std::endl;
}
};
class ScopedPtr {
private:
Entity* m_Obj;
public:
ScopedPtr(Entity* entity):m_Obj(entity) {}
~ScopedPtr() {
delete m_Obj;
}
Entity* GetObj() {
return m_Obj;
}
const Entity* operator->() const {
return m_Obj;
}
};
使用智能指针的情况下要使用重载符号,不然只能手动编写Get方法来获取对象指针(记得使用const):
ScopedPtr entity = new Entity();
entity->Print();//方法中要记得带const
entity.GetObj()->Print();
虽然下面两条语句意思一样,但使用智能指针可以自动释放内存,更为方便~
const ScopedPtr entity = new Entity();
const Entity* entity = new Entity();
当把数据序列化为一串字节流的时候,当你想要计算某些东西的偏移量的时候,会用到下面代码:
struct Vector3 {
float x, z, y;//找出y在内存中的偏移量4
};
int offset = (int)&((Vector3*)nullptr)->z;
std::cout << offset << std::endl;
动态数组
缓存线上存储更快,尽量使用对象而不是指针,指针是最后的选择。
动态数组指vector,声明语句:
#include<vector>
std::vector<Vertex> vertices;
添加语句:
vertices.push_back({1,2,3});
遍历语句(如果是类对象,需要重载<<操作符):
std::ostream& operator<<(std::ostream& stream, const Vertex& vertex) {
stream << vertex.x << "," << vertex.y << "," << vertex.z;
return stream;
}
for (int i = 0; i < vertices.size();i++) {
std::cout << vertices[i] << std::endl;
}
for (Vertex& v : vertices) {
std::cout << v << std::endl;
}//不加引用符就会复制
删除某个数据内容:
vertices.erase(vertices.begin() + 1);
清空数组:
vertices.clear();
全部代码如下:
#include<iostream>
#include<string>
#include<vector>
struct Vertex {
float x, y, z;
};
std::ostream& operator<<(std::ostream& stream, const Vertex& vertex) {
stream << vertex.x << "," << vertex.y << "," << vertex.z;
return stream;
}
void Function(const std::vector<Vertex>& vertices){
}
int main() {
std::vector<Vertex> vertices;
//缓存线上存储更快,尽量使用对象而不是指针,指针是最后的选择
vertices.push_back({1,2,3});
vertices.push_back({4,5,6});
Function(vertices);
for (int i = 0; i < vertices.size();i++) {
std::cout << vertices[i] << std::endl;
}
vertices.erase(vertices.begin() + 1);
for (Vertex& v : vertices) {
std::cout << v << std::endl;
}//不加引用符就会复制
vertices.clear();
std::cin.get();
}
数组优化:
struct Vertex {
float x, y, z;
Vertex(float x, float y, float z) : x(x), y(y), z(z) {}
Vertex(const Vertex& vertex) : x(vertex.x), y(vertex.z), z(vertex.z) {
std::cout << "Copied!" << std::endl;
}
};
std::ostream& operator<<(std::ostream& stream, const Vertex& vertex) {
stream << vertex.x << "," << vertex.y << "," << vertex.z;
return stream;
}
std::vector<Vertex> vertices;
vertices.push_back(Vertex({ 1, 2, 3 }));
vertices.push_back(Vertex({ 1, 2, 3 }));
vertices.push_back(Vertex({ 1, 2, 3 }));
运行结果:
为什么会发生六次呢?首先要先弄清楚,在main函数中,是一个栈来存储的,要想将vertex复制到vector中,这是三个对象分别copied的问题,其次是在数组扩容时需要修改之前的存储对象的指向。优化策略如下:
int main() {
std::vector<Vertex> vertices;
//缓存线上存储更快,尽量使用对象而不是指针,指针是最后的选择
vertices.reserve(3);
vertices.emplace_back(1,2,3);
vertices.emplace_back(4,5,6);
vertices.emplace_back(7,8,9 );
//vertices.push_back(Vertex({ 7,8,9 }));
std::cin.get();
}
分别使用reserve语句预设了空间,和vertices.emplace_back(1,2,3);直接构造对象,结果指针没有复制,那emplace_back岂不是任何时候都可以替换push_back,答案当然不是,因此查询了相关资料,弄清楚了两者的区别。
在某些情况下,使用emplace_back
可能比push_back
更高效,因为它避免了不必要的拷贝或移动操作。然而,并非所有情况都适合使用emplace_back
。如果对象的构造过程非常简单,或者对象的复制或移动成本很低,那么使用push_back
可能更合适。