根据我在几个地方读到的内容可能不太明智(这可能是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()。现在我的一些问题是:
感谢您的耐心和帮助!
答案 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的命题