实验目的
1. 验证左值与右值的区别;验证右值引用的使用;
2. 设计并验证移动构造函数和移动复制函数的定义与使用,及其带来的效率提升;
实验内容
1. 参考std::string,设计并实现MyString类。
基本功能要求:
1)四种构造函数、析构函数;
2)重载赋值操作符=,使其正确完成String类型的赋值操作;
3)重载必要的操作符,实现形如 String a = “Hello ”; a += “World!”;的功能。
4)重载必要的操作符,实现形如 String a, b, c; c = a + b; 的操作过程。
5)重载必要的操作符,当完成
String a(“Hello ”);
a << “world”;
的操作过程后,a所代表的字符串为”Hello world”。
6)重载必要的操作符,当完成
String a(“test”);
std::cout << a; 的操作过程后,在屏幕上输入 test
7)重载必要的操作符,当完成
String a(“test”);
a[2] = ’S’; 后,a所代表的字符串为”teSt”。
设计及性能要求:
尽可能的高效实现上述MyString类。要给出关于设计思想的描述或是对设计过程做出必要说明和解释。
2.编写相应的测试用例,验证上述功能(包括内存使用的正确性)。
3.编写相应的测试用例,验证移动语义带来的效率提升,并给出分析与解释。
实验过程
MyString.h:
//
// Created by HASEE on 2022/5/30.
//
#ifndef UNTITLED_TEST_1_H
#define UNTITLED_TEST_1_H
using namespace std;
class MyString{
private:
char *data; // 指向数组首地址
int len;
public:
MyString();
MyString(const char *const);
MyString(const MyString &a);
MyString(MyString&& mstr) noexcept ;
~MyString();
//重载<<运算符
MyString & operator <<(const char *);
friend ostream & operator <<(ostream &out,const MyString &s);
//重载=运算符
MyString & operator=( char *str);
MyString & operator=(const MyString &s);
//重载+ +=运算符
MyString & operator +( char *str);
MyString & operator +(const MyString &s);
MyString & operator +=( char *str);
MyString & operator +=(const MyString &s);
//重载[]
char & operator [](int i);
};
#endif //UNTITLED_TEST_1_H
MyString.cpp
//
// Created by HASEE on 2022/5/30.
//
#include <iostream>
#include "MyString.h"
#include <cstring>
#include <cassert>
using namespace std;
MyString::MyString() {//构造空字符串
data =new char[1];
data[0]='\0';
len=0;
}
MyString::MyString(const char *const p) //按照动态指针来构造相应字符串
{
if(p){
len= strlen(p); //取长度
data =new char[len+1];//开辟空间
strcpy(data,p);//复制值
} else{
MyString(); //如果传入的字符串为空,则直接调用缺省值构造函数
}
printf("constructor\n");
}
MyString::MyString(const MyString& otherMyString) {//拷贝构造函数,这里的形参使用了const,该形参类中的所有函数都要使用const来修饰
len=otherMyString.len;
data=new char[len+1]; //重新开辟空间
strcpy(data,otherMyString.data);
printf("copy constructor\n");
}
MyString::~MyString() {
delete[] data;
}
MyString::MyString(MyString&& mstr) noexcept {
if (&mstr == this){
printf("move constructor\n");
return ;
}
len=mstr.len;
data=mstr.data;
printf("move constructor\n");
}
ostream & operator << (ostream &out, const MyString &s) {
out << s.data;
return out;
}
MyString &MyString::operator=(char *str) {
delete[] data;
len= strlen(str);
data=new char[len+1];
strcpy(data,str);
return *this;
}
MyString &MyString::operator=(const MyString &s) {
if(&s ==this){//如果自己赋值给自己
return *this;
}
delete[] data; //释放原有内存
len=s.len;
data=new char[len+1];
strcpy(data,s.data);
return *this ;
}
MyString &MyString::operator+(char *str) {
char *t=data;//记录this.data
len=len+ strlen(str);
data=new char [len+1];
strcpy(data,t);
strcat(data,str);//将src的字符串追加到dest结尾
delete[] t; //内存释放
return *this ;
}
MyString &MyString::operator+(const MyString &s) {
char *t=data;
len=len+s.len;
data=new char [len+1];
strcpy(data,t);
strcat(data,s.data);
delete[] t; //内存释放
return *this;
}
MyString & MyString::operator+=(char *str) {
char *t = data;
len=len+ strlen(str);
data=new char[len+1];
strcpy(data,t);
strcat(data,str);
delete[] t;
return *this;
}
MyString & MyString::operator+=(const MyString &s) {
char *t = data;
len=len+s.len;
data=new char[len+1];
strcpy(data,t);
strcat(data,s.data);
delete[] t;
return *this;
}
MyString &MyString::operator<<(const char *s) {
return *this+s;
}
char &MyString::operator[](int i) {
assert(i >=0&&i<=len); //界限判断
return *(data+i);
}
MyString foo() {
return MyString{"woooohooooo"};
}
测试:
int main() {
printf("+=:");
MyString s="Hello";
s+="World!";
cout<<s<<endl;
printf("a+b:");
MyString d="Nihao";
MyString f="Ni";
MyString g="hao";
cout<<f+g<<endl;
printf("a << world:");
MyString a("hello");
a << "world";
cout<<a<<endl;
printf("std::cout:");
MyString b("test");
std::cout << b;
printf("\n[]重载:");
MyString c("test");
c[2]= 'S';
cout<<c;
}
测试结果:
测试二 移动语义带来的效率提升
有移动语义:
void time(){
auto start=std::chrono::steady_clock::now();
for(int i=0;i<1000000;++i){
MyString str =foo();
}
auto end=std::chrono::steady_clock::now();
auto duration =static_cast<std::chrono::duration<double,std::ratio<1,1000>>>(end-start);
std::cout<<"time is"<<duration.count() <<"ms";
}
int main() {
time();
}
结果:
没有移动语义:
原因:当要复制的对象是一个右值,会调用移动构造函数,其它情况调用复制构造函数。移动构造函数的参数没有使用关键字,参数的变量名前有两个&符号,用来表示右值引用。因为是右值引用,我们可以认为在之后它不会再被使用,所以移动构造函数直接复制了对象数据的内存指针,没有进行内存分配和数据的深copy。