c++ - 实现隐式转换为 char* 的 String 类 (C++)

标签 c++ string c++11 casting

根据我在几个地方读到的内容,这可能是不可取的(这可能是 std::string 还没有这样做的原因),但在受控环境中并谨慎使用,我认为它编写一个字符串类可能没问题,它可以在第三方库方法需要时隐式转换为适当的可写 char 缓冲区(仅将 char* 作为参数),并且仍然表现得像具有 Find() 等方法的现代字符串, Split(), SubString() 等。虽然我可以稍后尝试实现通常的其他字符串操作方法,但我首先想问一下执行此主要任务的高效和安全方法。目前,我们必须分配一个 char 数组,其大小大约是第三方方法预期的 char* 输出的最大大小,将其传递到那里,然后将返回的 char* 转换为 std::string 以便能够使用它允许的便捷方法,然后使用 string.c_str() 再次将其 (const char*) 结果传递给另一个方法。这既冗长又使代码看起来有点乱。

到目前为止,这是我最初的实现:

MyString.h

#pragma once
#include<string>

using namespace std;

class MyString
{
private:
    bool mBufferInitialized;
    size_t mAllocSize;
    string mString;
    char *mBuffer;

public:
    MyString(size_t size);
    MyString(const char* cstr);
    MyString();
    ~MyString();
    operator char*() { return GetBuffer(); }
    operator const char*() { return GetAsConstChar(); }
    const char* GetAsConstChar() { InvalidateBuffer(); return mString.c_str(); }

private:
    char* GetBuffer();
    void InvalidateBuffer();
};

MyString.cpp

#include "MyString.h"

MyString::MyString(size_t size)
    :mAllocSize(size)
    ,mBufferInitialized(false)
    ,mBuffer(nullptr)
{
    mString.reserve(size);
}

MyString::MyString(const char * cstr)
    :MyString()
{
    mString.assign(cstr);
}

MyString::MyString()
    :MyString((size_t)1024)
{
}

MyString::~MyString()
{
    if (mBufferInitialized)
        delete[] mBuffer;
}

char * MyString::GetBuffer()
{
    if (!mBufferInitialized)
    {
        mBuffer = new char[mAllocSize]{ '\0' };
        mBufferInitialized = true;
    }

    if (mString.length() > 0)
        memcpy(mBuffer, mString.c_str(), mString.length());

    return mBuffer;
}

void MyString::InvalidateBuffer()
{
    if (mBufferInitialized && mBuffer && strlen(mBuffer) > 0)
    {
        mString.assign(mBuffer);
        mBuffer[0] = '\0';
    }
}

示例用法(main.cpp)

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

void testSetChars(char * name)
{
    if (!name)
        return;
    //This length is not known to us, but the maximum 
    //return length is known for each function. 
    char str[] = "random random name";
    strcpy_s(name, strlen(str) + 1, str);
}


int main(int, char*)
{
    MyString cs("test initializer");
    cout << cs.GetAsConstChar() << '\n';
    testSetChars(cs);
    cout << cs.GetAsConstChar() << '\n';
    getchar();
    return 0;
}

现在,我计划在执行任何其他操作之前在几乎所有方法中调用 InvalidateBuffer()。现在我的一些问题是:

  1. 在内存/性能和/或安全方面是否有更好的方法,尤其是在 C++ 11 中(除了我计划很快添加的常用移动构造函数/赋值运算符)?
  2. 我最初使用字符的 std::vector 实现了“缓冲区”,这更容易实现并且更像 C++,但担心性能。所以 GetBuffer() 方法只会返回调整大小后的 vector 的起始指针。您认为在这里使用 vector 而不是 char* 有什么主要的优点/缺点吗?
  3. 我计划稍后为其添加宽字符支持。您认为两个结构的 union :{char,string} 和{wchar_t, wstring} 是否是实现该目的的方法(一次只能是这两个结构之一)?
  4. 它是否过于矫枉过正,而不是仅仅采用通常的方式传递 char 数组指针,转换为 std::string 并使用它来完成我们的工作。期望 char* 参数的第三方函数调用在代码中大量使用,如果它有效,我计划用这个新字符串完全替换 char* 和 std::string 。

感谢您的耐心等待和帮助!

最佳答案

如果我没理解错的话,你希望它起作用:

mystring foo;
c_function(foo);
// use the filled foo

带有 c_function喜欢...

void c_function(char * dest) {
  strcpy(dest, "FOOOOO");
}

相反,我建议这样做 ( ideone example ):

template<std::size_t max>
struct string_filler {
  char data[max+1];
  std::string & destination;
  string_filler(std::string & d) : destination(d) {
    data[0] = '\0'; // paranoia
  }
  ~string_filler() {
    destination = data;
  }
  operator char *() {
    return data;
  }
};

并像这样使用它:

std::string foo;
c_function(string_filler<80>{foo});

通过这种方式,您可以为 C 函数提供一个“正常”缓冲区,其中包含您指定的最大值(无论哪种方式,您都应该知道……否则调用该函数将是不安全的)。在破坏临时(根据标准,必须在函数调用的表达式之后发生)时,字符串被复制(使用 std::string 赋值运算符)到由 std::string 管理的缓冲区中.


解决您的问题:

Do you think there are any major pros/cons of using a vector instead of char* here?

是的:使用 vector 可以将您从手动内存管理中解放出来。这是一个巨大的优势。

I plan to add wide char support to it later. Do you think a union of two structs : {char,string} and {wchar_t, wstring} would be the way to go for that purpose (it will be only one of these two at a time)?

union 不是个好主意。您如何知道哪个成员当前处于事件状态?你需要在 union 之外有一面旗帜。你真的想让每根绳子都带着它吗?相反,看看标准库在做什么:它使用模板来提供这种抽象。

Is it too much overkill [..]

写一个字符串类? 是的,太多了。

关于c++ - 实现隐式转换为 char* 的 String 类 (C++),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39068844/

相关文章:

c++ - Ogre3D 中的类声明中 `_OgreExport` 的用途是什么?

javascript - javascript中的逻辑运算符&&和两个字符串

c - 将字符串的第一个字节递增一个

c++ - 从成员函数指针转换为另一种类型并返回,严格别名问题?

带 std::array 的 C++ 模板函数

c++ - 如何打印嵌套 std::unordered_map 的内容?

c++ - 防止在我的行 C++ 的末尾打印额外的空格

c++ - 为什么 `typeid(T&).name()` 的输出给出为 `T` 而不是 `T&` ?

c++ - 我可以为 Boost.Spirit.Qi 中的内存分配给列表运算符 (%) 提示吗?

string - 在 Clojure(Script) 中按行字母顺序对字符串进行排序