实现一个String类,隐式转换为char *(C ++)

时间:2016-08-21 21:03:30

标签: 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. 我最初实施了&#39;缓冲区&#39;使用chad :: vector of chars,它更容易实现,更像C ++,但是关注性能。所以GetBuffer()方法只返回调整大小的向量的开始指针。您认为在这里使用向量而不是char *有任何重大利弊吗?
  3. 我打算稍后再添加宽字符支持。你认为两个结构的联合:{char,string}和{wchar_t,wstring}是为了这个目的的方式(它一次只是这两个中的一个)?
  4. 是否过于矫枉过正,而不仅仅是通常传递char数组指针,转换为std :: string并使用它进行工作。第三方函数调用期望char *参数在代码中被大量使用,并且我计划用这个新字符串完全替换char *和std :: string(如果它有效)。
  5. 感谢您的耐心和帮助!

2 个答案:

答案 0 :(得分:2)

如果我理解正确,你希望这个有效:

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管理的缓冲区中。 / p>

解决您的问题:

  

你觉得在这里使用vector而不是char *有什么主要优点/缺点吗?

是:使用向量可以从手动内存管理中解放出来。这是一个巨大的专业人士。

  

我计划稍后添加宽字符支持。你认为两个结构的联合:{char,string}和{wchar_t,wstring}是为了这个目的(它一次只是这两个中的一个)吗?

工会是一个坏主意。你怎么知道哪个成员目前有效?你需要在联盟之外的旗帜。你真的希望每根弦都能随身携带吗?而是查看标准库正在做什么:它使用模板来提供这种抽象。

  

太过分了[...]

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

答案 1 :(得分:1)

您想要做的事情已经存在。例如,使用这个普通的旧C函数:

/**
 * Write n characters into buffer.
 * n cann't be more than size
 * Return number of written characters
 */
ssize_t fillString(char * buffer, ssize_t size);

从C ++ 11开始:

std::string str;
// Resize string to be sure to have memory
str.resize(80);
auto newSize = fillSrting(&str[0], str.size());
str.resize(newSize);

或没有先调整大小:

std::string str;
if (!str.empty()) // To avoid UB
{
    auto newSize = fillSrting(&str[0], str.size());
    str.resize(newSize);
}

但在C ++ 11之前,std::string并不能保证存储在单个连续内存块中。所以你必须先通过std::vector<char>;

std::vector<char> v;
// Resize string to be sure to have memor
v.resize(80);
ssize_t newSize = fillSrting(&v[0], v.size());

std::string str(v.begin(), v.begin() + newSize);

您可以轻松地使用Daniel的命题

相关问题