当它们在内存中表示时,C ++对象与C结构相同吗?
例如,使用C,我可以这样做:
struct myObj {
int myInt;
char myVarChar;
};
int main() {
myObj * testObj = (myObj *) malloc(sizeof(int)+5);
testObj->myInt = 3;
strcpy((char*)&testObj->myVarChar, "test");
printf("String: %s", (char *) &testObj->myVarChar);
}
我认为C ++不允许为内置+
类型重载char *
运算符。
所以我想创建自己的轻量级字符串类,它没有std::string
的额外开销。我认为std::string
是连续代表的:
(int)length, (char[])data
我想要完全相同的功能,但没有前缀长度(节省8个字节的开销)。
这是我用来测试的代码,但它导致了段错误
#include <iostream>
using namespace std;
class pString {
public:
char c;
pString * pString::operator=(const char *);
};
pString * pString::operator=(const char * buff) {
cout << "Address of this: " << (uint32_t) this << endl;
cout << "Address of this->c: " << (uint32_t) &this->c << endl;
realloc(this, strlen(buff)+1);
memcpy(this, buff, strlen(buff));
*(this+strlen(buff)) = '\0';
return this;
};
struct myObj {
int myInt;
char myVarChar;
};
int main() {
pString * myString = (pString *) malloc(sizeof(pString));
*myString = "testing";
cout << "'" << (char *) myString << "'";
}
编辑:没有人真正了解我想做什么。是的我知道我可以有一个指向类中字符串的指针,但比普通的cstring贵8个字节,我想要完全相同的内部表示。谢谢你试试
编辑:我想要实现的最终结果是能够使用+运算符,与使用strcat等相比没有额外的内存使用
const char * operator+(const char * first, const char * second);
答案 0 :(得分:16)
你不应该浪费你的时间来编写字符串类 - 这是人们花时间编写它们的原因,并且认为他们编写它们是天真的,因为他们想要创建大的混淆和过度的代码,你可以很容易地改进它们。小时问题。
例如,您的代码在赋值运算符中的内存重新分配具有二次复杂度 - 每次分配大于1个字符的sting将使用大于1个字节的新内存块,导致在“少量”分配之后出现大量内存碎片 - 您节省了几个字节,但可能会丢失兆字节来解决空间和内存页碎片问题。
同样以这种方式设计你无法有效地实现+ =运算符,而不是仅仅复制附加的字符串,在大多数情况下你总是需要复制整个字符串 - 因此如果你追加小字符串,再次达到二次复杂度几次到一个更大的。
很抱歉,但是你的想法看起来很有可能变得难以维持,并且比std :: string这样的典型字符串实现效率低一些。
别担心 - 对于“编写自己更好的标准容器版本”的所有好主意来说都是如此:)
答案 1 :(得分:8)
struct myObj {
//...
char myVarChar;
};
这不起作用。您需要一个固定大小的数组,一个指向char的指针或使用struct hack。您将无法为此myVarChar
分配指针。
所以我想创建自己的轻量级字符串类,它没有额外的开销std :: string has。
你指的是多少额外开销?您是否测试并测量过std::string
是否真的成为瓶颈?
我认为std :: string是连续表示的
是的,主要是字符缓冲部分。但是,以下内容:
标准不要求(int)的长度(字符[])的数据
。翻译:字符串实现不需要使用其数据的这种特定布局。它可能还有其他数据。
现在,您的轻量级字符串类出现了错误:
class pString {
public:
char c; // typically this is implementation detail, should be private
pString * pString::operator=(const char *);
// need ctors, dtors at least as well
// won't you need any functions on strings?
};
尝试以下方面的内容:
/* a light-weight string class */
class lwstring {
public:
lwstring(); // default ctor
lwstring(lwstring const&); // copy ctor
lwstring(char const*); // consume C strings as well
lwstring& operator=(lwstring const&); // assignment
~lwstring(); // dtor
size_t length() const; // string length
bool empty() const; // empty string?
private:
char *_myBuf;
size_t _mySize;
};
答案 2 :(得分:6)
哇。你要做的是彻底滥用C ++,如果它有效的话,将完全依赖于编译器,并且肯定会让你有一天在TheDailyWTF登陆。
你得到段错误的原因可能是因为你的operator =正在将对象重新分配到不同的地址,但是你没有更新main中的myString指针。我甚至在这一点上甚至称它为一个对象,因为从未调用任何构造函数。
我认为你要做的是让pString成为一个更聪明的指向字符串的指针,但是你的一切都错了。让我来解决它。
#include <iostream>
using namespace std;
class pString {
public:
char * c;
pString & operator=(const char *);
const char * c_str();
};
pString & pString::operator=(const char * buff) {
cout << "Address of this: " << (uint32_t) this << endl;
cout << "Address of this->c: " << (uint32_t) this->c << endl;
c = (char *) malloc(strlen(buff)+1);
memcpy(c, buff, strlen(buff));
*(c+strlen(buff)) = '\0';
return *this;
};
const char * pString::c_str() {
return c;
}
int main() {
pString myString;
myString = "testing";
cout << "'" << myString.c_str() << "'";
}
现在我不会使用malloc而是使用new / delete,但我保留了尽可能接近原始内容。
你可能会认为你是在浪费你班级中指针的空间,但你不是 - 你正在为你之前保留在主要指针中的指针进行交易。我希望这个例子说清楚 - 变量大小相同,malloc / realloc分配的额外内存量也是一样的。
pString myString;
char * charString;
assert(sizeof(myString) == sizeof(charString));
P.S。我应该指出,这段代码仍然需要很多工作,它充满了漏洞。你需要一个构造函数来初始化指针,一个析构函数可以在它完成时释放它,只是为了初学者。你也可以自己实现operator +。
答案 3 :(得分:2)
当它们在内存中表示时,对象C ++对象与C结构相同。
严格来说,没有。一般来说,是的。 C ++类和结构在内存布局上与C结构相同,除了:
我不认为C ++允许重载内置char *类型的+运算符。所以我想创建自己的轻量级字符串类,它没有额外的开销std :: string has。我认为std :: string是连续表示的
您可以为operator+
类型创建char*
重载。正常行为是指针算术。 std::string
重载operator+
以将char*
数据附加到字符串。该字符串作为C字符串存储在内存中,另外还有其他信息。 c_str()
成员函数返回指向内部char
数组的指针。
在您的C示例中,您依赖于未定义的行为。不要realloc
那样。它可能导致糟糕的事情 - 即奇怪的段错误。
你的C ++示例在执行realloc(this)
时也做了坏事。相反,您应该携带char*
并获取new char[]
缓冲区来存储字符而不是realloc()
。这种realloc
的行为未定义。
答案 4 :(得分:2)
您的班级定义/用法存在很多问题。如果要存储字符串,则应使用指针类型,如char * a member,而不是单个char。使用单个char意味着只分配了一个内存字符。
另一个错误是你对此进行重新分配的分配代码 - 你可以改变分配的内存,但不能改变它的值。您必须将结果分配给此以实现此目的(this = (*pString)realloc(this, strlen(buff+1));
),这无论如何都是非常糟糕的做法。在char *成员上使用realloc要好得多。
不幸的是C ++本身有no alternative for realloc or expand,你必须改为使用new和delete,自己做任何复制。
答案 5 :(得分:2)
为什么用C语言编写C语言,为什么不使用C ++?
答案 6 :(得分:2)
您无法在C或C ++中更改对象/结构的大小。它们的大小在编译时是固定的。
答案 7 :(得分:2)
我认为'this'不会像你认为的那样有效。
具体来说,你不能重新分配它以指向成员函数中的更大缓冲区,因为无论什么叫成员函数仍然有一个指向旧'this'的指针。由于它没有通过引用传递,因此无法更新它。
显而易见的是,你的类应该拥有一个指向缓冲区的指针并重新分配它。但是,重新实现字符串类是一种很好的方法,可以给自己带来很多麻烦。一个简单的包装函数可能会实现你想要的(假设“与使用strcat相比,能够使用没有额外内存使用量的+运算符”真的是你想要的):
void concatenate(std::string& s, const char* c) {
s.reserve(s.size() + strlen(c));
s.append(c);
}
无论如何,追加可能会在内部做到这一点。
答案 8 :(得分:1)
您无法重新分配C ++对象。正如其他人所指出的那样this
并不是一个你可以修改的指针,但不能保证它会指向一个区域realloc
具有访问权限。
连接的一个解决方案是实现一个类层次结构,它将推迟实际连接直到需要它。
像这样的东西
class MyConcatString;
class MyString {
public:
MyString(const MyConcatString& c) {
reserve(c.l.length()+c.r.lenght());
operator = (l);
operator += (r);
}
MyConcatString operator + (const MyString& r) const {
return MyConcatString(*this, r);
}
};
class MyConcatString {
public:
friend class MyString;
MyConcatString(const MyString& l, const MyString& r):l(l), r(r) {};
...
operator MyString () {
MyString tmp;
tmp.reserve(l.length()+r.length());
tmp = l;
tmp += r;
return tmp;
}
private:
MyString& l;
MyString& r;
}
所以,如果你有
MyString a = "hello";
MyString b = " world";
MyString c = a + b;
会变成 MyString c = MyConcatString(a,b);
有关详细信息,请查看“C ++编程语言”。
其他解决方案是将char *包装在struct中,但你似乎不喜欢这个想法。
但无论您选择何种解决方案,C ++中的对象都无法重新定位。
答案 9 :(得分:1)
您想要做的不是也不能在C ++中工作。您正在寻找的是灵活阵列的C99功能。这在C99中运行良好有两个原因,首先你没有内置构造函数,第二个你没有继承(至少不是语言特性)。如果一个类继承自另一个类,则子类使用的内存由后面的父类的内存打包,但灵活的数组需要在结构/类的末尾。
class pString {
char txt[];
}
class otherString : pString { // This cannot work because now the
size_t len; // the flexible array is not at the
} // end
拿pd :: string它是由C ++专家编写的,我敢肯定他们没有理由没有留下“好招”。如果您仍然发现它们在您的程序中表现不佳,请使用纯C字符串,当然,它们不提供您想要的甜蜜API。
答案 10 :(得分:1)
#include <iostream>
using namespace std;
class pString {
public:
char c;
pString * pString::operator=(const char *);
};
pString * pString::operator=(const char * buff) {
cout << "Address of this: " << (uint32_t) this << endl;
cout << "Address of this->c: " << (uint32_t) &this->c << endl;
char *newPoint = (char *)realloc(this, strlen(buff)+1);
memcpy(newPoint, buff, strlen(buff));
*((char*)newPoint+strlen(buff)) = '\0';
cout << "Address of this After: " << (uint32_t) newPoint << endl;
return (pString*)newPoint;
};
int main() {
pString * myString = (pString *) malloc(sizeof(pString));
*myString = "testing";
cout << "Address of myString: " << (uint32_t) myString << endl;
cout << "'" << (char *) myString << "'";
}
Works当realloc不重新分配指针时,即
地址:1049008 地址 - >&gt; c:1049008 此地址后:1049008 myString的地址:1049008'testing'
可以使用,但是当发生以下情况时,它会失败
地址:1049008 地址 - >&gt; c:1049008 此地址:1049024 myString的地址:1049008''
显而易见的解决方案是
this = (pString*) newPoint;
但编译器抱怨赋值中的左值无效。有没有人更新这个正确的方法(只是为了完整性,我怀疑我会使用代码,因为每个人似乎都讨厌它)。感谢
答案 11 :(得分:1)
您正在移动“this”指针。那不行。 我认为你真正想要的只是缓冲区的包装。
答案 12 :(得分:1)
不介意缺乏const正确性,因为这是一个模拟,但是如何:
class light_string {
public:
light_string(const char* str) {
size_t length = strlen(str);
char* buffer = new char[sizeof(size_t) + length + 1];
memcpy(buffer, &length, sizeof(size_t));
memcpy(buffer + sizeof(size_t), str, length);
memset(buffer + sizeof(size_t) + length, 0, 1);
m_str = buffer + sizeof(size_t);
}
~light_string() {
char* addr = m_str - sizeof(size_t);
delete [] addr;
}
light_string& operator =(const char* str) {
light_string s = str;
std::swap(*this, s);
return *this;
}
operator const char*() {
return m_str;
}
size_t length() {
return
*reinterpret_cast<size_t *>(m_str - sizeof(size_t));
}
private:
char* m_str;
};
int main(int argc, char* argv[])
{
cout<<sizeof(light_string)<<endl;
return 0;
}
答案 13 :(得分:1)
#include <iostream>
using namespace std;
class pString {
public:
char c[1];
pString * pString::operator=(const char *);
};
pString * pString::operator=(const char * buff) {
cout << "Address of this: " << (uint32_t) this << endl;
cout << "Address of this->c: " << (uint32_t) &this->c << endl;
realloc(this->c, strlen(buff)+1);
memcpy(this->c, buff, strlen(buff));
*(this->c+strlen(buff)) = '\0';
return this;
};
struct myObj {
int myInt;
char myVarChar;
};
int main() {
pString * myString = (pString *) malloc(sizeof(pString));
*myString = "testing vijay";
cout << "'" << ((char*)myString << "'";
}
This should work. But its not advisable.
答案 14 :(得分:1)
如果你想要一些与std::string
基本相同的东西,除了它不知道字符串有多长,你应该知道std::string
如何工作,它有多少运算符重载等等。然后模仿,只需要你想要的差异。
然而,不太可能有任何真正的意义。
关于您的最新更新 - 您说您想要一种设计,其中一般应用程序代码将传递给堆对象的裸指针。没有自动清理。
这很简单,这是一个非常糟糕的主意。
答案 15 :(得分:0)
这段代码很乱,RnR和其他人建议不建议。但它适用于我想要它做的事情:
#include <iostream>
using namespace std;
struct pString {
/* No Member Variables, the data is the object */
/* This class cannot be extended & will destroy a vtable */
public:
pString * pString::operator=(const char *);
};
pString& operator+(pString& first, const char *sec) {
int lenFirst;
int lenSec = strlen(sec);
void * newBuff = NULL;
if (&first == NULL)
{
cout << "NULL" << endl;
lenFirst = 0;
newBuff = malloc(sizeof(pString)+lenFirst+lenSec+1);
} else {
lenFirst = strlen((char*)&first);
newBuff= (pString*)realloc(&first, lenFirst+lenSec+1);
}
if (newBuff == NULL)
{
cout << "Realloc Failed"<< endl;
free(&first);
exit(0);
}
memcpy((char*)newBuff+lenFirst, sec, lenSec);
*((char*)newBuff+lenFirst+lenSec) = '\0';
cout << "newBuff: " << (char*)newBuff << endl;
return *(pString*)newBuff;
};
pString * pString::operator=(const char * buff) {
cout << "Address of this: " << (uint32_t) this << endl;
char *newPoint = (char *)realloc(this, strlen(buff)+200);
memcpy(newPoint, buff, strlen(buff));
*((char*)newPoint+strlen(buff)) = '\0';
cout << "Address of this After: " << (uint32_t) newPoint << endl;
return (pString*)newPoint;
};
int main() {
/* This doesn't work that well, there is something going wrong here, but it's just a proof of concept */
cout << "Sizeof: " << sizeof(pString) << endl;
pString * myString = NULL;
//myString = (pString*)malloc(1);
myString = *myString = "testing";
pString& ref = *myString;
//cout << "Address of myString: " << myString << endl;
ref = ref + "test";
ref = ref + "sortofworks" + "another" + "anothers";
printf("FinalString:'%s'", myString);
}
答案 16 :(得分:0)
如果你想要表现,你可以像这样编写你的课程:
template<int max_size> class MyString
{
public:
size_t size;
char contents[max_size];
public:
MyString(const char* data);
};
在上下文中将max_size初始化为适当的值。通过这种方式,可以在堆栈上创建对象,并且不涉及内存分配。
可以通过重载新运算符来创建所需的内容:
class pstring
{
public:
int myInt;
char myVarchar;
void* operator new(size_t size, const char* p);
void operator delete(void* p);
};
void* pstring::operator new(size_t size, const char* p)
{
assert(sizeof(pstring)==size);
char* pm = (char*)malloc(sizeof(int) + strlen(p) +1 );
strcpy(sizeof(int)+pm, p);
*(int*)(pm) = strlen(p); /* assign myInt */
return pm;
}
void pstring::operator delete(void* p)
{
::free(p);
}
pstring* ps = new("test")pstring;
delete ps;