C++
October 6, 2018

C++实现一个简单的String类

C++实现一个简单的String类

使用基本的C++知识实现一个简单的String类,这个类中包含了C++常用的知识点。感觉是很有意思的一个小代码片段。

跟大家分享一下我的实现,欢迎大家批评指正。

类声明

  1. 该类中包含了三个构造函数:有参数的构造函数,拷贝构造函数已经移动构造函数

  2. 重载了[],=(一个普通赋值运算符,一个移动赋值运算符),+,==四个运算符

  3. 一个用于求字符长度的方法;一个用于获取C语言类型字符串的方法

  4. 以友元的方式重载了输入流>>和输出流<<操作符

头文件(strings.h)

//
// Created by Zhenyu Tan on 2018/10/3.
//
#include <iostream>

class String {
private:
    char* _buffer;
    size_t _length;
    void init(const char* str);

public:
    String(const char* str= nullptr);       // 默认构造函数
    String(const String& other);            // 拷贝构造函数
    String(String&& other) noexcept;        // 移动构造函数
    ~String();                              // 析构函数

    size_t length();
    const char* data();

    char& operator[](size_t index);
    String& operator=(const String& other);
    String& operator=(String&& other) noexcept;
    String operator+(const String& other);
    bool operator==(const String& other);

    friend std::ostream& operator<<(std::ostream& output, const String& str);
    friend std::istream& operator>>(std::istream& input, String& str);
};

类实现

源文件(strings.cpp)

//
// Created by Zhenyu Tan on 2018/10/5.
//

#include "strings.h"
#include <cstring>
#include <exception>
#include <iostream>

using std::cout;
using std::ostream;
using std::istream;

size_t String::length() {
    if (0 == _length) {
        _length = std::strlen(_buffer);
    }
    return _length;
}

const char* String::data() {
    return _buffer;
}


void String::init(const char* str) {
    if (nullptr == str) {
        _length = 0;
        _buffer = nullptr;
    } else {
        _length = std::strlen(str);
        _buffer = new char[_length + 1];
        std::strcpy(_buffer, str);
    }
}


String::String(const char* str) {
    init(str);
    cout << "默认构造函数(" << *this << ")\n";
}


String::String(const String& other) {
    // 在类的成员函数中可以访问同类型实例的私有变量
    init(other._buffer);
    cout << "拷贝构造函数(" << *this << ")\n";
}

String::String(String&& other) noexcept {
    // 把other对象掏空用来填充this
    _buffer = nullptr;
    _buffer = other._buffer;
    _length = other._length;
    other._buffer = nullptr;
    other._length = 0;
    cout << "移动构造函数(" << *this << ")\n";
}


String::~String() {
    delete[] _buffer;
    cout << "析构函数(" << *this << ")\n";
}

/*
 * 拷贝构造函数使用传入对象的值生成一个新的对象的实例
 * 赋值运算符是将对象的值复制给一个已经存在的实例
 */
String& String::operator=(const String& other) {
    if (this != &other) {
        delete[] _buffer;
        init(other._buffer);
    }
    cout << "拷贝赋值操作(" << *this << ")\n";
    return *this;
}

/*
 * 移动赋值操作即把参数传进来的对象的所有权转移到this指向的对象
 * 掏空other对象的所有
 */
String& String::operator=(String&& other) noexcept {
   if (this != &other) {
       _buffer = nullptr;
       _buffer = other._buffer;
       _length = other._length;
       other._buffer = nullptr;
       other._length = 0;
   }
    cout << "移动赋值操作(" << *this << ")\n";
    return *this;
}


char& String::operator[](size_t index) {
    if (index >= _length) {
        throw std::out_of_range("Index out of range");
    } else {
        return _buffer[index];
    }
}


bool String::operator==(const String& other) {
    if (_length != other._length) {
        return false;
    } else {
        return 0 == std::strcmp(_buffer, other._buffer);
    }
}

/*
 * 关于是返回对象本身还是返回对象引用
 * 如果函数返回在函数中创建的临时对象,则不要使用引用
 * 如果函数返回的是通过引用或指针传递给它的对象,则应当按引用返回对象
 * 如果先创建一个对象,然后返回改对象的副本,则可以使用返回对象
 */
String String::operator+(const String& other) {
    String _str;
    if (nullptr == _buffer) {
        _str = other;
    } else if (nullptr == other._buffer) {
        _str = *this;
    } else {
        _str._buffer = new char[_length + other._length + 1];
        std::strcpy(_str._buffer, _buffer);
        std::strcat(_str._buffer, other._buffer);
        _str._length = std::strlen(_str._buffer);
    }
    return _str;
}


ostream& operator<<(ostream &output, const String& str) {
    if (nullptr == str._buffer) {
        output << "";
    } else {
        output << str._buffer;
    }
    return output;
}

istream& operator>>(istream &input, String& str) {
    input >> str._buffer;
    return input;
}

调用示例

#include "strings.h"
#include <iostream>

using std::cout;

int main() {
    String str1("Hello");
    cout << str1.data() << '\n';
    cout << str1.length() << '\n';
    cout << "----------\n";
    String str2 = "Word";
    cout << str2 << '\n';
    cout << "----------\n";
    String str3 = str1 + str2;
    cout << str3.data() << '\n';
    cout << str3.length() << '\n';
    return 0;
}

运行结果:

默认构造函数(Hello)
Hello
5
----------
默认构造函数(Word)
Word
----------
默认构造函数()
HelloWord
9
析构函数(HelloWord)
析构函数(Word)
析构函数(Hello)

主程序中的第7行和第11行各自调用一次默认的有参构造函数,第14行是重载的加法运算符中调用了一次无参的构造函数(由于C++编译器的优化,函数返回值没有调用拷贝构造函数)