代码仅供参考
基本功能已实现,代码可自由复制、修改、转载。
课程名称: 面向对象技术(C++)
实验项目: 移动语义
实验仪器: 计算机
实验目的
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类。要给出关于设计思想的描述或是对设计过程做出必要说明和解释。
(一)函数
#include <iostream>
#include<string.h>
#include <chrono>
using namespace std;
class MyString{
public:
char *_str{nullptr};
int _length=0;
public:
int getl(){
return _length;
}
MyString()=default;
~MyString(){//析构函数
delete[] _str;
};
MyString(const char* str){
if(str==nullptr)
return;
_length=strlen(str);
_str=new char[_length+1];
strcpy(_str,str);
}
//拷贝构造函数
MyString(const MyString& other):_length{other._length}{
if(other._str==nullptr)return;
_str=new char[_length+1];
strcpy(_str,other._str);
}
//移动构造
MyString(MyString &&other):_length{other._length},_str{other._str}{
other._length=0;
other._str=nullptr;
}
MyString operator+(const MyString &str){
MyString result;
result._length=_length+str._length;
result._str=new char[result._length+1];
strcpy(result._str,_str);
strcat(result._str,str._str);
return result;
}
MyString operator +=(char * str){
_length=strlen(str)+_length;
char *t=_str;
_str=new char[_length+1];
strcpy(_str,t);
strcat(_str,str);
delete []t;
return *this;
}
MyString& operator=(const MyString &str){
delete []_str;
_length=str._length;
_str=new char[_length+1];
strcpy(_str,str._str);
return *this;
}
MyString &operator=(char *str){
_length=strlen(str);
delete [] _str;
_str=new char[_length+1];
strcpy(_str,str);
return *this;
}
friend std::ostream& operator<<(std::ostream& os, const MyString& str) {
os << str._str;
return os;
}
char& operator[] (int index) {
if (index < 0 || index >= _length) {
cout << "索引越界\n";
exit(1);
}
return _str[index];
}
};
int main()
{
MyString s1;
s1=("哥们名叫丁真!");
cout<<s1<<endl;
MyString s2="Smoke cigarettes by rules,";
cout<<s2<<endl;
s2+="我的小马名字叫珍珠。";
cout<<s2<<endl;
MyString a,b,c;
b="假烟发现就跑路\n";
c="Baby I ain't smoking by your rules";
a=b+c;
cout<<a<<endl;
MyString s3("臭要饭的别挡我财路\n");
s3<<"Why you always so poor";
cout<<s3<<endl;
MyString end("zood");
cout<<end<<endl;
end[0]='Z';
cout <<end;
return 0;
}
2.编写相应的测试用例,验证上述功能(包括内存使用的正确性)。
3.编写相应的测试用例,验证移动语义带来的效率提升,并给出分析与解释。
- 实验过程
1)构造函数:接受一个 char* 类型的字符串作为参数,创建一个 MyString 类型的对象。
MyString(const char* str){
if(str==nullptr)
return;
_length=strlen(str);
_str=new char[_length+1];
strcpy(_str,str);
}
2)拷贝构造函数:接受一个 MyString 类型的对象作为参数,创建一个新的 MyString 类型的对象,它的值与参数对象相同。
//拷贝构造函数
MyString(const MyString& other):_length{other._length}{
if(other._str==nullptr)return;
_str=new char[_length+1];
strcpy(_str,other._str);
}
- 移动构造函数:接受一个右值引用的 MyString 类型对象作为参数,创建一个新的 MyString 类型的对象,它的值与参数对象相同,并将参数对象的指针置为 null。
//移动构造
MyString(MyString &&other):_length{other._length},_str{other._str}{
other._length=0;
other._str=nullptr;
}
- 拷贝赋值运算符:接受一个 MyString或char* 类型的对象作为参数,将当前对象的值设置为参数对象的值。
MyString operator +=(char * str){
_length=strlen(str)+_length;
char *t=_str;
_str=new char[_length+1];
strcpy(_str,t);
strcat(_str,str);
delete []t;
return *this;
}
MyString& operator=(const MyString &str){
delete []_str;
_length=str._length;
_str=new char[_length+1];
strcpy(_str,str._str);
return *this;
}
MyString &operator=(char *str){
_length=strlen(str);
delete [] _str;
_str=new char[_length+1];
strcpy(_str,str);
return *this;
}
5)连接赋值运算符:接受一个 char* 类型的字符串作为参数,将其拼接到当前对象的末尾,并返回当前对象的引用。
MyString operator +=(char * str){
_length=strlen(str)+_length;
char *t=_str;
_str=new char[_length+1];
strcpy(_str,t);
strcat(_str,str);
delete []t;
return *this;
} }
- 输出运算符:重载 << 运算符,用于将 MyString 类型的对象输出到标准输出流。MyString operator<< (char *str){
_length=strlen(str)+_length;
char *t=_str;
_str=new char[_length+1];
strcpy(_str,t);
strcat(_str,str);
delete []t;
return *this;
索引运算符:重载 [] 运算符,接受一个整数类型的索引作为参数,返回当前对象中对应位置的字符。
public:friend std::ostream& operator<<(std::ostream& os, const MyString& str) {
os << str._str;
return os;
}
- 功能验证
int main()
{
MyString s1;
s1=("哥们名叫丁真!");
cout<<s1<<endl;
MyString s2="Smoke cigarettes by rules,";
cout<<s2<<endl;
s2+="我的小马名字叫珍珠。";
cout<<s2<<endl;
MyString a,b,c;
b="假烟发现就跑路\n";
c="Baby I ain't smoking by your rules";
a=b+c;
cout<<a<<endl;
MyString s3("臭要饭的别挡我财路\n");
s3<<"Why you always so poor";
cout<<s3<<endl;
MyString end("zood");
cout<<end<<endl;
end[0]='Z';
cout <<end;
return 0;
}
性能测试
MyString s1("Hello");
int n_iterations = 10000000;
{
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < n_iterations; ++i) {
MyString s2(s1); //调用拷贝构造函数
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
std::cout << "Time taken by " << n_iterations << " copy constructor calls: "
<< 1000.0 * elapsed_seconds.count() << "ms\n";
}
{
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < n_iterations; ++i) {
MyString s3(std::move(s1)); //调用移动构造函数
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
std::cout << "Time taken by " << n_iterations << " move constructor calls: "
<< 1000.0 * elapsed_seconds.count() << "ms\n";
}