C++ 简易string类实现(一)

最近在重温C++ Effective系列,在more effective C++中Item28~30涉及一个string类的实现,为了深入地理解,故由简单到复杂实现一个简易的string类,这一篇里,不考虑书中的3个条款,在后续的博客里,逐步将条款的内容加入进来,实现一些相对复杂的特性;
string类声明:

class String
{
    friend std::ostream& operator<<(std::ostream& os, const String&);

    friend std::istream& operator>>(std::istream& is, String&);

    friend bool operator < (const String& left_, const String& right_);

    friend bool operator > (const String& left_, const String& right_);

    friend bool operator != (const String& left_, const String& right_);

    friend bool operator == (const String& left_, const String& right_);

    friend String operator + (const String& left_, const String& right_);
public:
    String(const char* str_ = nullptr);

    String(const String& str_);

    String& operator=(const String& str_);

    String& operator=(const char* str_);

    ~String();

public:
    inline size_t size() const;

    inline const char* c_str() const;

    inline void swap(String& str_);

public:
    String& operator += (const String&);

    String& operator += (const char*);

    const char& operator[](size_t index_) const;

    char& operator[](size_t index_);

private:
    char* _ptr;
    size_t _size;
};

string类实现:

String::String(const char* str_/* = nullptr */)
{
    if (str_ == nullptr)
    {
        _ptr = new char[1];
        _ptr[0] = '\0';
        _size = 0;
    }
    else
    {
        size_t len = strlen(str_) + 1;
        _ptr = new char[len];
        strcpy(_ptr, str_);
        _size = len - 1;
    }
}

String::String(const String& str_)
    : String(str_._ptr) 
    //调用上个构造函数完成构造,降低代码重复
{
    /*_ptr = new char[str_._size + 1];
    strcpy(_ptr, str_._ptr);
    _size = str_._size;*/
}

String& String::operator=(const String& str_)
{
    if (this == &str_)
    {
        return *this;
    }
    return operator=(str_._ptr);
}

String& String::operator=(const char* str_)
{
    //异常安全
    /*String tmp(str_);
    swap(tmp);*/
    swap(String(str_));
    return *this;
}

String::~String()
{
    if (_ptr != nullptr)
    {
        delete[] _ptr;
        _ptr = nullptr;
        _size = 0;
    }

}

size_t String::size() const
{
    return _size;
}

const char* String::c_str() const
{
    return _ptr;
}

void String::swap(String& str_)
{
    std::swap(_ptr, str_._ptr);
    std::swap(_size, str_._size);
}

String& String::operator+=(const String& str_)
{
    return operator+=(str_.c_str());
}

String& String::operator+=(const char* str_)
{
    //strcpy(dst, src)函数,需保证src不为nullptr
    if (str_ == nullptr || *str_ == '\0')
    {
        return *this;
    }
    //异常安全
    size_t len = _size + strlen(str_) + 1;
    char* tmp = new char[len];
    strcpy(tmp, _ptr);
    strcat(tmp, str_);
    std::swap(_ptr, tmp);
    _size = len - 1;
    delete[] tmp;

    return *this;
}

const char& String::operator[](size_t index_) const
{
    if (index_ >= _size)
    {
        throw std::out_of_range("String : out of index!");
    }
    return _ptr[index_];
}

//调用const版本的operator[],降低代码重复
//反过来,不建议用const调用non-const,这
//样可能会导致潜在的bug
char& String::operator[](size_t index_)
{
    return const_cast<char&>(
        (static_cast<const String&>(*this))[index_]);
}

std::ostream& operator<<(std::ostream& os, const String& str_)
{
    os << str_._ptr;
    return os;
}

std::istream& operator>>(std::istream& is, String& str_)
{
    //预分配1个字节
    char* str = (char*)malloc(sizeof(char) * 1);
    size_t capacity = 1;    //记录当前分配空间的容量
    char* buff = str;

    //预处理流的所有空格与回车
    //*buff = getchar() 或者is.get(*buff)均可
    while ((*buff = getchar()) == ' ' || *buff == '\n');
    //while (is.get(*buff) && (*buff == ' ' || *buff == '\n'));

    int index = 1;
    while (1)
    {
        //回车跳出
        if (*buff == '\n')
        {
            *buff = '\0';
            break;
        }
        //空格跳出
        else if (*buff == ' ')
        {
            *buff = '\0';
            break;
        }
        //空间不足
        else if (index == capacity)
        {
            //容量加倍,并分配对应容量的空间
            capacity *= 2;
            str = (char*)realloc(str, capacity);
        }
        //为了避免realloc返回首地址改变,不使用++buff,而是str+偏移量
        buff = (str + index);
        *buff = getchar();
        //is.get(*buff);    //每次读取一个字符
        index++;
    }
    //输入完成
    str_ = String(str);
    free(str);
    str = nullptr;
    buff = nullptr;

    return is;
}

bool operator < (const String& left_, const String& right_)
{
    bool less = true;
    int index = 1;
    for (; index < left_.size() && index < right_.size(); ++index)
    {
        if (left_[index] != right_[index])
        {
            less = left_[index] < right_[index];
            break;
        }
    }
    if (index == left_.size() || index == right_.size())
    {
        less = left_.size() < right_.size();
    }
    return less;
}

bool operator > (const String& left_, const String& right_)
{
    return (left_ != right_) && !(left_ < right_);
}

bool operator == (const String& left_, const String& right_)
{
    if (left_.size() != right_.size())
    {
        return false;
    }

    bool equal = true;
    for (int i = 0; i < left_.size(); ++i)
    {
        if (left_[i] != right_[i])
        {
            equal = false;
            break;
        }
    }
    return equal;
}

bool operator != (const String& left_, const String& right_)
{
    return !(left_ == right_);
}

String operator + (const String& left_, const String& right_)
{
    String tmp(left_);
    tmp += right_;
    return tmp;
}

需要注意的地方:
1.赋值运算符重载里,需要判断赋值对象是否是自身,不过,在我的实现中,通过swap函数保证异常安全,即使没有以上的判断,也能正常运行;
2.friend std::istream& operator>>(std::istream& is, String&)函数,实现不能是简单的如下代码:

friend std::istream& operator>>(std::istream& is, String& str_)
{
    is>>str_._ptr;
    return is;
}

因为,参数str_可能是一个通过默认构造函数构造的对象,其 ptr指针指向的空间仅有一个字符(’\0’),is>>str . _ptr向这个空间写入数据,会导致溢出,在VS2013平台,虽然该函数能”正常”运行,但在该String对象被析构时,会因delete[] _ptr而出错;
3.为了最大可能消除代码冗余,函数执行效率会下降,例如operator>就需要调用operator<和operator=,这里是在代码的冗余度,维护难度和执行效率间的协调间偏向前两者;
4.friend std::istream& operator>>(std::istream& is, String&)的实现参考了如下链接:
https://zhidao.baidu.com/question/711251936107526885.html
5.operator[]函数,通过non-const版本调用const版本,消除代码冗余,这是在某本书上学习到的方法(具体书名忘记了);

由于在C++上实践不多,加上算法能力弱,如果有实现的代码有bug或者设计问题,希望可以评论中提出来,非常感谢^-^

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RPC(Remote Procedure Call)是一种远程调用协议,它允许客户端程序调用远程服务器上的函数或方法。C++可以使用一些库来实现RPC服务,其中比较流行的有 gRPC 和 Apache Thrift。 以下是一个使用 gRPC 的简易 RPC 服务的示例: 1. 首先,需要安装 gRPC 和 Protocol Buffers: ``` sudo apt install -y build-essential autoconf libtool pkg-config grpc libgrpc++-dev protobuf-compiler-grpc ``` 2. 创建一个 Protocol Buffers 文件 `example.proto`,定义 RPC 服务的接口: ``` syntax = "proto3"; package example; service ExampleService { rpc SayHello (HelloRequest) returns (HelloResponse) {} } message HelloRequest { string name = 1; } message HelloResponse { string message = 1; } ``` 3. 使用 Protocol Buffers 编译器生成 C++ 代码: ``` protoc --grpc_out=. --cpp_out=. example.proto ``` 4. 实现 RPC 服务的接口: ``` #include <iostream> #include <memory> #include <string> #include <grpcpp/grpcpp.h> #include "example.grpc.pb.h" using grpc::Server; using grpc::ServerBuilder; using grpc::ServerContext; using grpc::Status; using example::HelloRequest; using example::HelloResponse; using example::ExampleService; class ExampleServiceImpl final : public ExampleService::Service { Status SayHello(ServerContext* context, const HelloRequest* request, HelloResponse* response) override { std::string prefix("Hello "); response->set_message(prefix + request->name()); return Status::OK; } }; void RunServer() { std::string server_address("0.0.0.0:50051"); ExampleServiceImpl service; grpc::ServerBuilder builder; builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); builder.RegisterService(&service); std::unique_ptr<Server> server(builder.BuildAndStart()); std::cout << "Server listening on " << server_address << std::endl; server->Wait(); } int main(int argc, char** argv) { RunServer(); return 0; } ``` 5. 编译并运行服务器代码: ``` g++ -std=c++11 -I. -I/usr/local/include -L/usr/local/lib example.pb.cc example.grpc.pb.cc example_server.cc -lgrpc++ -lgrpc -lgpr -lprotobuf -lpthread -o example_server ./example_server ``` 6. 编写客户端代码: ``` #include <iostream> #include <memory> #include <string> #include <grpcpp/grpcpp.h> #include "example.grpc.pb.h" using grpc::Channel; using grpc::ClientContext; using grpc::Status; using example::HelloRequest; using example::HelloResponse; using example::ExampleService; class ExampleClient { public: ExampleClient(std::shared_ptr<Channel> channel) : stub_(ExampleService::NewStub(channel)) {} std::string SayHello(const std::string& name) { HelloRequest request; request.set_name(name); HelloResponse response; ClientContext context; Status status = stub_->SayHello(&context, request, &response); if (status.ok()) { return response.message(); } else { return "RPC failed"; } } private: std::unique_ptr<ExampleService::Stub> stub_; }; int main(int argc, char** argv) { ExampleClient client(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials())); std::string name("World"); std::string reply = client.SayHello(name); std::cout << "Received: " << reply << std::endl; return 0; } ``` 7. 编译并运行客户端代码: ``` g++ -std=c++11 -I. -I/usr/local/include -L/usr/local/lib example.pb.cc example.grpc.pb.cc example_client.cc -lgrpc++ -lgrpc -lgpr -lprotobuf -lpthread -o example_client ./example_client ``` 以上是一个简易的使用 gRPC 实现的 RPC 服务和客户端的示例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值