string vector

string vector

改成上图才能正确识别

改成上图才能正确识别

拷贝构造 现代写法

下图左传统 下图右现代

this 指向当前正在构造 / 操作的对象实例

成员变量也如左图一样

this 指向的对象,调用对应类的成员函数并传入参数

利用string tmp(s.str)会调用接收char*类型的构造函数(字符指针构造,不是默认构)得到tmp(帮我们完成原来我们手动需要完成的数据处理),

上图this是s2的。调用方s1,是传入函数的参数s

代码进行了交换,如果内置类型没有初始化是随机值,交换给tmp会有问题。因为tmp出了作用域后会析构,释放随机野指针 → 程序崩溃。所以这里将成员变量初始化。tmp销毁空指针

编译器在编译阶段,就计算好了这个类单个实例的总占用字节。在创建(使用构造函数)该类的对象之前,不管是栈、堆、全局区,都必须已经分配好完整的单个实例的总占用字节,这是对象的存储载体。

free,delete,delete[]空时直接返回,不会报错

赋值= 现代写法

调用接收char*类型的构造函数和拷贝构造都行

拷贝构造参数必须是引用。如果是传值传参(不是引用),那么会调用拷贝构造,拷贝构造又会调用传值传参,无穷递归。

赋值参数不用必须是引用,是传值传参,那么会调用拷贝构造,正好将s3拷贝构造给tmp,然后直接交换即可

s1原本的空间就需要释放,交还给tmp,tmp出了作用域后会自动调用析构函数

swap

自己手动模拟 库里模板

以交换两个自定义对象为例:

直接套库里模板会有三次深拷贝,伴随开空间拷贝数据析构等,代价太大。交换内置类型或无深拷贝对象就行

自己手动模拟的也是套库里模板,只不过交换的类型都是内置类型,效率明显提升

库里还有一个全局swap函数,与模板同名。所以出现swap情况下优先调用该函数。

一、标准库提供的全局std::swap(string& x, string& y)内部实现就是一行:

void swap(string& x, string& y) { x.swap(y); }
  1. 标准库内部已经给std::string写好了类内成员函数swap()
  2. 同时库中预实现了全局重载std::swap(string&, string&)
  3. 你直接写代码std::swap(s1, s2)就能调用,底层自动执行s1.swap(s2)
  4. x.swap(y)代表:用对象 x 调用自身的成员 swap 函数,传入对象 y

二、标准库里没有为你的自定义string类提前写好swap 重载

如果什么都不写,直接调用std::swap(s1,s2),会触发通用模板:

template<typename T> void swap(T& a, T& b) { T temp = a; // 完整拷贝整个字符串,复制堆上所有字符,低效 a = b; b = temp; }

性能极差,违背 swap 轻量化设计。

三、想要让std::swap(s1,s2)高效交换你的自定义 string,必须自己实现类内成员 swap

// 写在你的自定义string类里面 void swap(string& s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); }

只要有这个成员函数,调用std::swap(s1,s2)时,编译器依靠 ADL 查找规则,优先调用该函数,会自动执行s1.swap(s2),编译器优先去bit命名空间、string类内调用模拟实现的成员函数 swap,和标准文档描述行为一致,只交换三个内置变量,O (1) 开销