【发布时间】:2016-08-21 21:03:29
【问题描述】:
根据我在几个地方读到的内容,这可能是不可取的(这可能是 std::string 尚未这样做的原因),但在受控环境和谨慎使用的情况下,我认为它可能可以编写一个字符串类,当第三方库方法(仅接受 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()。现在我的一些问题是:
- 在内存/性能和/或安全性方面是否有更好的方法,尤其是在 C++ 11 中(除了我计划很快添加到其中的通常的移动构造函数/赋值运算符)?
- 我最初使用字符的 std::vector 实现了“缓冲区”,它更容易实现并且更像 C++,但担心性能。所以 GetBuffer() 方法只会返回调整大小的向量的开始指针。您认为在这里使用向量而不是 char* 有什么主要的优点/缺点吗?
- 我计划稍后为其添加宽字符支持。你认为两个结构的 union : {char,string} 和 {wchar_t, wstring} 将是实现该目的的方式(一次只能是这两个结构中的一个)?
- 是否过于矫枉过正,而不仅仅是按照通常的方式传递 char 数组指针,转换为 std::string 并使用它来完成我们的工作。期望 char* 参数的第三方函数调用在代码中被大量使用,如果它有效,我计划用这个新字符串完全替换 char* 和 std::string。
感谢您的耐心和帮助!
【问题讨论】:
-
“根据我在几个地方读到的内容,这可能是不可取的” 我会再写一遍:这看起来是个相当糟糕的主意。
-
与第三方库一起使用时,为什么不直接写
var.c_str()?在我看来,只要从 std::string 调用c_str()就可以轻松安全地解决很多工作 -
为什么不添加一个小的适配器接口来模仿第三方库中的所有函数,只接受不作为参数并返回 std::string 的 char 指针?它对所有现有代码都是透明的,没有裸字符指针的麻烦。
-
std::string已经允许你通过char*: stackoverflow.com/questions/38702943/… -
最重要的一点是完全不清楚
char *p = someString;在看代码的时候做了什么。\0终止了吗?我可以给它写信吗?它指向多少内存?也允许使用if(someString)和someString + 5;等无意义的代码,容易隐藏bug。