问题:
值类型在c++中是一个很重要的概念,指的是一个类的实例像int类型一样,可以被复制、构造、传递,而且不需要用户去担心(至少在表面上)new和delete的问题。
那么构造一个完整的值类型需要注意哪些方面呢?
让我们一字符串类型来做一个简单的设计。
首先我们要明确这个类型的实例可以有哪些操作,我们就要通过构造函数和重载函数实现这些操作。
#include<iostream>
#include<cmath>
#include<iomanip>
#include<vector>
using namespace std;
struct String
{
public:
String();
String(const char *);
String(const String &);
String(String &&);
~String();
String & operator=(const String &);
String & operator=(String &&);
bool operator==(const String &) const;
bool operator!=(const String &) const;
bool operator<(const String &) const;
bool operator<=(const String &) const;
bool operator>(const String &) const;
bool operator>=(const String &) const;
const char *GetBuffer() const;
size_t GetLength() const;
String operator+(const String &) const;
String SubString(int start, int length) const;
};
下边是一个简单的字符串值类型实现;
在写这种代码的时候,最好用到单元测试(更大的叫TDD(测试驱动开发,用测试来驱动软件的开发))
参考书籍
用这种方法的作用:
#include <iostream>
#include <cstdlib>
#include <cstring>
#ifndef _MSC_VER
#define strcpy_s(DST,SIZE,SRC) strcpy((DST),(SRC))
#endif
#define JSK_ASSERT(EXPRESSION) if(!(EXPRESSION)) throw 0
using namespace std;
struct String
{
private:
size_t length;
char* buffer;
public:
String():length{0},buffer{new char[1]{'\0'}}
{
}
String(const char* theBuffer):length{strlen(theBuffer)},buffer{new char[length+1]}
{
strcpy_s(buffer,length+1,theBuffer);
cout << " =";
}
String(const String& theString):length{theString.length},buffer{new char[theString.length+1]}
{
strcpy_s(buffer,length+1,theString.buffer);
}
String(String&& theString)
:length{ theString.length }
, buffer{ theString.buffer }
{
theString.buffer = nullptr;
}
~String()
{
delete[] buffer;
}
String& operator=(const String& theString)
{
if (this != &theString)
{
delete[] buffer;
length = theString.length;
buffer = new char[length+1];
strcpy_s(buffer,length+1,theString.buffer);
}
return *this;//这里返回的类型是左值引用类型,并不会在返回时创建零时对象,而且this相对于这个重载函数是一直存在的并不是函数里的局部变量,所以可以返回左值引用类型
}
String& operator=(String&& theString)
{
if (this != &theString)
{
delete[] buffer;
length = theString.length;
buffer = theString.buffer;
theString.buffer = nullptr;
}
return *this;
}
const char* GetBuffer()const
{
return buffer;
}
size_t GetLength()const
{
return length;
}
};
int main()
{
{
String s;//调用String()默认构造函数
JSK_ASSERT(s.GetBuffer() != nullptr);
JSK_ASSERT(s.GetBuffer()[0] == '\0');
JSK_ASSERT(s.GetLength() == 0);
}
{
String s = "This is a string!";//调用 String(const char* theBuffer)初始化(非缺省)构造函数
JSK_ASSERT(s.GetBuffer() != nullptr);
JSK_ASSERT(strcmp(s.GetBuffer(), "This is a string!") == 0);
JSK_ASSERT(s.GetBuffer()[17] == '\0');
JSK_ASSERT(s.GetLength() == 17);
}
{
String s = "This is a string!";
String t = s;//调用 String(const String& theString)复制构造函数
JSK_ASSERT(t.GetBuffer() != nullptr);
JSK_ASSERT(s.GetBuffer() != t.GetBuffer());
JSK_ASSERT(s.GetLength() == t.GetLength());
JSK_ASSERT(strcmp(s.GetBuffer(), t.GetBuffer()) == 0);
}
{
String s = "This is a string!";
s = s;//调用等号运算符重载函数String& operator=(const String& theString)
JSK_ASSERT(s.GetBuffer() != nullptr);
JSK_ASSERT(strcmp(s.GetBuffer(), "This is a string!") == 0);
JSK_ASSERT(s.GetBuffer()[17] == '\0');
JSK_ASSERT(s.GetLength() == 17);
}
{
String s = "This is a string!";
String t;
t = s;//调用等号运算符重载函数String& operator=(const String& theString)
JSK_ASSERT(t.GetBuffer() != nullptr);
JSK_ASSERT(s.GetBuffer() != t.GetBuffer());
JSK_ASSERT(s.GetLength() == t.GetLength());
JSK_ASSERT(strcmp(s.GetBuffer(), t.GetBuffer()) == 0);
}
{
String s = "This is a string!";
String t = std::move(s);//调用String(String&& theString),转移复制构造函数(也即右值引用复制构造函数)
JSK_ASSERT(s.GetBuffer() == nullptr);
JSK_ASSERT(t.GetBuffer() != nullptr);
JSK_ASSERT(t.GetLength() == 17);
JSK_ASSERT(strcmp(t.GetBuffer(), "This is a string!") == 0);
}
{
String s = "This is a string!";
s = std::move(s);
JSK_ASSERT(s.GetBuffer() != nullptr);
JSK_ASSERT(strcmp(s.GetBuffer(), "This is a string!") == 0);
JSK_ASSERT(s.GetBuffer()[17] == '\0');
JSK_ASSERT(s.GetLength() == 17);
}
{
String s = "This is a string!";
String t;
t = std::move(s);//调用转移操作符重载函数String& operator=(String&& theString)(也即右值引用操作符重载函数)
JSK_ASSERT(s.GetBuffer() == nullptr);
JSK_ASSERT(t.GetBuffer() != nullptr);
JSK_ASSERT(t.GetLength() == 17);
JSK_ASSERT(strcmp(t.GetBuffer(), "This is a string!") == 0);
}
return 0;
}