【41】运算符及其重载
运算符时我们使用给的一种符号,通常代替一个函数来执行一些事情。比如加减乘除、dereference运算符、箭头运算符、+=运算符、&运算符、左移运算符、new和delete、逗号、圆括号、方括号等等等等。
重载这个术语本质上是给运算符重载赋予新的含义,或者添加参数,或者创建,允许在程序中定义或更改运算符的行为。
运算符就是函数。运算符重载是一个非常有用的特性,但在Java等语言中不受支持,它在C#等语言中得到部分支持,C++给了我们完全的控制权。
不用运算符实现加、乘
struct Vector2 //向量结构体
{
float x, y;
Vector2(float x, float y)
: x(x),y(y) {}
Vector2 Add(const Vector2& other) const
{
return Vector2(x + other.x, y + other.y);
}
Vector2 Multiply(const Vector2& other) const
{
return Vector2(x * other.x, y * other.y);
}
};
int main()
{
Vector2 position(4.0f, 4.0f);
Vector2 speed(0.5f, 1.5f);
Vector2 powerup(1.1f, 1.1f);
Vector2 result1 = position.Add(speed.Multiply(powerup)); //position+(speed*powerup),Java中只能这么用
Vector2 result2 = position.+ speed * powerup; //用运算符定义,运算符同样存在优先级
std::cin.get();
加入运算符后
Vector2 operator+(const Vector2& other)const
{
return Add(other);
}
int main()
{
Vector2 position(4.0f, 4.0f);
Vector2 speed(0.5f, 1.5f);
Vector2 powerup(1.1f, 1.1f);
Vector2 result1 = position.Add(speed.Multiply(powerup)); //position+(speed*powerup),Java中只能这么用
Vector2 result2 = position.+ speed * powerup; //用运算符定义,运算符同样存在优先级
std::cin.get();
左移运算符
std::ostream& operator<<(std::ostream& stream, const Vector2& other) // 重载左移运算符
{
stream << other.x << "," << other.y;
return stream;
}
std::cout << result2 << std::endl;
【42】this关键字
通过this可以访问成员函数,成员函数是一个属于某个类的函数或者方法,在方法内部可以引用this。
this是一个指向当前对象实例的指针,该方法属于这个对象实例。
链接:link
【43】对象生存期
内存以及对象是如何在栈上生存的,生存期对于基于栈的变量意味着什么。
每当我们在c++中进入一个作用域,我们是在push栈帧,它不一定非得是将数据push进一个栈帧。
栈上变量自动销毁,在很多方面都很有用,可以帮助我们自动化代码。比如类的作用域,比如智能指针unique_ptr,这是一个作用域指针,或者像作用域(scoped_lock)。
但最简单的例子可能是作用域指针,他基本上是一个类,它是一个指针的包装器,在构造时用堆分配指针,然后在析构时删除指针,所以我们可以自动化这个new和delete。
【44】智能指针
智能指针本质上是原始指针的包装。当你创建一个智能指针,它会调用new并为你分配内存,然后基于你使用的智能指针,这些内存会在某一时刻自动释放。
unique_ptr
他的构造函数时explicit,所以需要显式调用构造函数。作用域结束,Entity就会被自动摧毁。
unique_ptr 指针不能被复制,,因为只要作用域结束了,在堆上分配的内存也会被释放掉,因此在该指针在实现上强制地将拷贝构造函数和拷贝构造符都删除,避免错误的行为。
但是为了异常安全,一般我们使用std::make_unique
#include <memory>
class Entity
{
public:
Entity()
{
std::cout << "created" << std::endl;
}
~Entity()
{
std::cout << "destoryed" << std::endl;
}
void Print() {}
};
int main()
{
{
std::unique_ptr<Entity> entity(new Entity());
//std::unique_ptr<Entity> entity = std::make_unique<Entity>(); //为了异常安全,一般我们使用std::make_unique
entity->Print();
}
std::cin.get();
}
share_ptr
和 unique_ptr 由于异常安全的原因而不推荐使用 new 的方式创建智能指针不同,因为 shared_ptr 需要分配另一块内存,称为控制块,用来存储引用计数,所以如果使用 new 的形式来创建一个对象再将其传给 shared_ptr 的构造函数,他就必须做两次内存分配。所以需要用make_share将他们组合在一起,这样更有效率。
share_ptr是可以复制的
std::shared_ptr<Entity> shareEntity = std::make_shared<Entity>();
std::shared_ptr<Entity> e0 = shareEntity; //share_ptr可以复制
weak_ptr
和复制 shared_ptr 所做的一样,但当你把 shared_ptr 赋值给一个 weak_ptr,假如唯一的那个 shared_ptr 指针死去,内存就会被删除,因为赋值给 weak_ptr 并不会增加引用数量。例如你在排序一个集合,你不需要关注指针是否有效,你只需要存储一个他们的引用就好了。
std::weak_ptr<Entity> entity = std::make_weak<Entity>();
智能指针选择顺序
优先选择unique_ptr ,因为有一个较低的开销,如果需要分享,则选择share_ptr。
【45】复制与copy构造函数
复制普通变量与复制指针是不同的。
在栈中创建地对象,其数值存储在不同地内存地址,当赋值发生时,就是将值准确地拷贝到对方地内存那里。但在堆中创建的对象,我们所持有的变量是一个指针,这个指针存储着内存地址,并指向堆中的内存,当我们进行赋值时,实际上改变的是地址,也就是两个变量都将指向同一块内存了。
struct Vector2
{
float x, y;
};
int main()
{
Vector2* a = new Vector2(); //在堆上创建指针
Vector2* b = a; // a,b指向相同的内存地址
b++; //只改变b,a指针是完整的
b->x = 2; //访问内存地址,设置为某个值,同时影响a,b
std::cin.get();
复制字符串
class String
{
private:
char* m_Buffer;
unsigned int m_Size;
public:
String(const char* string)
{
m_Size = strlen(string);
m_Buffer = new char[m_Size+1]; //数组大小要+1,留给止位符
memcpy(m_Buffer, string, m_Size); //复制函数memcpy[destination,source,size]
}
~String()
{
delete[] m_Buffer ;
}
//友元函数,声明一个友元函数
friend std::ostream& operator<<(std::ostream& stream, const String& string);
};
//打印字符串的函数,重载左移操作符
std::ostream& operator<<(std::ostream& stream, const String& string)
{
stream << string.m_Buffer();
return stream;
}
int main()
{
String string = "cherno";
std::cout << string << std::endl;
std::cin.get();
}
浅拷贝:复制的是地址。如果其中一个调用了析构函数并且删除了堆上内存,当另一个在调用析构函数试图删除内存时,就会抛出异常,因为同一块内存不能被销毁两次。
浅拷贝是新增了一个指针指向内存,深拷贝是复制内存。
String string = "cherno";
String second = string; //浅拷贝,复制的是地址,即string和second指向同一段内存
深拷贝
想要实现深拷贝,希望被赋值的变量拥有自己的唯一的内存块,有自己的指针,就需要使用到拷贝构造函数,该构造函数会在你赋值复制时调用。
//c++默认提供的拷贝构造函数
//执行深copy必须使用的代码
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); //从other对象复制
}
建议总是通过const & 去传递对象!!!!