Everytime I encounter the situation dealing with c string, I'm very confused.
why are those two prints have same result?
In my understaning, first function assigns the address of string at text variable. which seems proper to me. But the second function assigns the address at where text variable points to. what happened here?
#include <iostream>
#include <cstring>
void getText(char** text) {
*text = strdup("AAAAA");
}
void getText2(char* text) {
text = strdup("AAAAA");
}
int main()
{
char* text;
getText(&text);
std::cout << text << std::endl; // prints "AAAAA"
getText2(text);
std::cout << text << std::endl; // prints "AAAAA"
}
This function
void getText2(char* text) {
text = strdup("AAAAA");
}
has a memory leak.
Function parameters are function local variables.
You can imagine the definition of the function getText2 and its call the following way. I renamed the function parameter that it would be more clear.
getText2(text);
//...
void getText2( /*char* parm_text */) {
char *parm_text = text;
parm_text = strdup("AAAAA");
}
The local variable that is the parameter parm_text will be destroyed after exiting the function. However the allocated memory in this statement
parm_text = strdup("AAAAA");
is not freed.
On the other hand the argument itself was not changed. The function used the value stored in the argument that was assigned to the local variable.
You could declare the parameter as reference to the argument. For example
void getText2(char* &text) {
^^^^^
text = strdup("AAAAA");
}
In this case it is the argument itself that is changed in the function.
As for the function
void getText(char** text) {
*text = strdup("AAAAA");
}
then the argument is passed indirectly by using a pointer to the argument. So inside the function the value of the argument is changed.
In the first case, you passed a pointer to your local pointer, you dereference that pointer and make it point to the value returned by strdup(), you are modifying the address the original pointer points to.
In the second one, you pass the poiner itself, you cannot alter it inside the function because even if the two pointers point to the same memory initially, they are stored in different places, so altering the address of one doesn't affect the other.
If you alter the data the pointer points to, and not the address in getText2() then it will change, like
text[0] = 'B';
text[1] = 'B';
text[2] = 'B';
text[3] = 'B';
text[4] = 'B';
You should also call free() after you use the pointer returned by strdup() or it will be a memory leak.
Finally, using pointers in c++ is currently considered bad practice unless you are a library programmer, which I don't think is the case. Instead, use std::string and all the c++ concepts (like pass by reference which doesn't exist in c) that will allow you to write modern c++ programs.
Passing by reference in c++ is possible
void getText(std::string &text)
{
text = "AAAAAA";
}
void getText2(std::string &text)
{
text = "BBBBBB";
}
int main()
{
std::string text;
getText(text);
std::cout << text << std::endl;
getText2(text);
std::cout << text << std::endl;
return 0;
}
There you go, no memory leaks, it works as expected, and it's modern c++.
I am trying to learn C++ and was writing programs to learn Copy Constructors and Operator overloading. I am surprised that the below program when Copy constructor is used doesn't crash saying "Double Free", whereas when using Operator = overloading crashes consistently.
#include <iostream>
using namespace std;
class XHandler
{
public:
XHandler()
{
data = new char[8];
strcpy(data, "NoName");
}
XHandler (const char *str)
{
data = new char (strlen(str) + 1 );
strcpy (data, str);
}
XHandler (const XHandler &xh)
{
data = xh.data;
}
XHandler& operator = (const XHandler &xh)
{
data = xh.data;
}
~XHandler()
{
delete data;
}
void debug()
{
cout << data <<endl;
}
private:
char *data;
};
int main()
{
XHandler wm("hello"), wb("there");
wm.debug();
wb.debug();
XHandler wc (wm);
wc.debug();
XHandler wd;
wd = wc;
wd.debug();
}
Note that in both copy constructor and operator overloading, I am just copying the 'data' pointer from one object to another. When the destructor is invoked for 'wd' and 'wc', it crashes the program consistently. If I comment the below lines and execute only the copy constructor, the program doesn't crash at all. I would expect since both 'wc's and 'wm's data variable are also pointing to the same pointer, the program would crash.
XHandler wd;
wd = wc;
wd.debug();
I understand double delete is undefined behaviour. But, what I wonder is it consistently crashes in one way and doesn't in the other.
The behavior of "Undefined behavior" is just undefined. That means, no one put effort into that cases because that cases should not happen. Even a working program can be "Undefined behavior".
In your special case of an double delete, what happens, really depends on the implementation of the allocator. A usual implementation is to put released memory into a list and a next allocation will be satisfied with an element out of this list. If you double delete a chunk of memory, it will be added to the list twice. And then two allocation will be satisfied with the very same chunk of memory and thus two object will be created at the same memory location.
BTW: You destructor is already broken, as it doesn't use the array delete.
I wirte this piece of test code, hope it will help here, I think crush is related to platform a lot, it is not just by chance, since this piece of code crush in linux ,but works fine in codeblock IDE
#include <iostream>
#include <cstring>
#include <stdio.h>
using namespace std;
class XHandler
{
public:
XHandler()
{
data = new char[8];
strcpy(data, "NoName");
std::cout<< "default construcor is called" << std::endl;
}
XHandler (const char *str)
{
data = new char [strlen(str) + 1 ];
strcpy (data, str);
std::cout<< "param construcor is called" << std::endl;
}
XHandler (const XHandler &xh)
{
data = xh.data;
std::cout<< "copy construcor is called" << std::endl;
}
XHandler& operator = (const XHandler &xh)
{
data = xh.data;
std::cout<< "operator construcor is called" << std::endl;
return *this;
}
~XHandler()
{
std::cout<< "destrucor is called" << std::endl;
print_dir();
if (data)
{
delete [] data;
data = NULL;
std::cout<< "delete data" << std::endl;
}
}
void debug()
{
cout << data <<endl;
}
void print_dir()
{
printf("location: %p\n",data);
}
private:
char *data;
};
int main()
{
XHandler wm("hello"), wb("there");
wm.debug();
wb.debug();
wm.print_dir();
wb.print_dir();
XHandler wc (wm);
wc.print_dir();
wc.debug();
XHandler wd;
wd = wc;
wd.debug();
}
it crush in linux gcc4.1.2 ,it outputs
param construcor is called
param construcor is called
hello
there
location: 0x502010
location: 0x502030
copy construcor is called
location: 0x502010
hello
default construcor is called
operator construcor is called
hello
destrucor is called
location: 0x502010
delete data
destrucor is called
location: 0x502010
*** glibc detected *** ./test: double free or corruption (fasttop): 0x0000000000502010 ***
Your program has undefined behavior.
Problems:
data = new char (strlen(str) + 1 );
This doesn't allocate strlen(str) + 1 characters. Instead, it allocates a single character and places the strlen(str) + 1 value within. When you perform a strcpy afterwards, you are corrupting the stack through buffer overflow (writing strlen(str) -1 chars past the allocated space). To allocate an array you should use square brackets:
data = new char [strlen(str) + 1 ];
Second, you should delete the data with delete[] data;. Otherwise, you get undefined behavior.
Third, your strcpy code doesn't copy the NULL terminator of the string. You should add a null terminator:
data[ strlen(str) ] = 0;
after the strcpy lines; (see comments).
Additionally, there is no requirement that an application would crash on double delete. Deleting the same memory twice is UB.
So I've this issue I can't get fixed :-(
In my .h I've this:
protected:
char* _textPath_1;
FileReader* _reader_3;
in .cpp I've:
_reader_3 = new FileReader();
_textPath_1 = "foo";
_reader_3->openFile(_textPath_1);
And FileReader has this:
private:
char* fileName;
public:
signed int openFile(char* name);
but If I write this (just to test):
signed int FileReader::openFile(char* name) {
std::cout << name << std::endl;
fileName = name;
stream.open(fileName, std::ios::in);
if (!stream.is_open()) {
FileReader::printErrorOpeningFile(fileName);
stream.close();
return -1;
}
return 0;
}
fileName is a char * and I need that it gets the same value (foo) of name. I get an error, and I'm not even able to print name, it just print a blank line.. why?
EDIT: it's not working even using strcpy.. Actually inside the function I can't print the value of name, it's like it has been "deininitialized"
You need to allocate space for your text string _textPath_1.
Try this instead.
char myTextString[] = "foo";
_textPath_1 = myTextString;
This creates a local character array (a character string), which is initialized to "foo\0". It then copies that character string's address to your char pointer _textPath_1. As a LOCAL storage, it will only be valid in the local code block and will not be usable once your code has dropped out of its scope. If you need that string past the local code block, you will need to allocate it from heap memory (using new for instance) and remember to deallocate it after you are done with it.
You cannot use strcpy with your unallocated pointer because strcpy expects the destination char* to be pointing at a character array acting as your destination string buffer. As you haven't allocated any char space at all, it cannot copy "foo" into your _textPath_1, and that's why you get a runtime error when you try to strcpy it.
These and other fun with char* is why std::string was invented. No worries about allocating and deallocating space, having to use strcpy to copy its value, etc etc etc. Consider using std::string _textPath_1 in place of your char* _textPath_1.
You have to allocate _reader_3 before calling the function.
FileReader* _reader_3 = new FileReader;
I assume fileName is your member variable. Accessing pointers without initialization will result in unpredictable results
If you're really defining global variables in your header file:
char* _textPath_1;
FileReader* _reader_3;
Then you shouldn't be doing that. Global variables should be declared in header files, but defined in an implementation file.
This code works fine:
#include <iostream>
#include <fstream>
struct FileReader {
char* fileName;
std::fstream stream;
signed int FileReader::openFile(char* name) {
std::cout << name << std::endl;
fileName = name;
stream.open(fileName, std::ios::in);
if (!stream.is_open()) {
FileReader::printErrorOpeningFile(fileName);
stream.close();
return -1;
}
return 0;
}
void printErrorOpeningFile(char *) {}
};
int main() {
char* _textPath_1;
FileReader* _reader_3;
_reader_3 = new FileReader();
_textPath_1 = "foo";
_reader_3->openFile(_textPath_1);
delete _reader_3;
}
I'm trying to write a piece of code for a kind of tree I'm making, and if a node doesn't exist i would like to throw an exception showing what node doesn't exist. Ie:
Trie t = Trie();
try {
t.get('a');
} catch(NoSuchNode e) {
cout << e.what() << endl;
}
this code should just do nothing if the node exists, otherwise print the error given, here's my code for the exception (i use the std::exception because that seemed like a good idea):
class NoSuchNode : public std::exception
{
private:
char *_node_name;
public:
NoSuchNode(const char *node_name) { _node_name = new char(*node_name); }
virtual const char* what() const throw()
{
std::stringstream ss;
ss << "There exists no node for: " << _node_name << ".";
const std::string& tmp = ss.str();
return tmp.c_str();
}
};
And this works as it should, valgrind doesn't give any errors on invalid reads either so it's fine i suppose? Now to my question, before i came up with this solution i had just changed:
_node_name = new char(*node_name);
to:
_node_name = node_name;
so that i don't need to allocate more space for it. The problem is that sometimes it doesn't really print any good data and sometimes the one char and even more after that. Not really what i would have expected. Also, valgrind complained that i read past the memory i could use. Why is this? My bet is that if std::string just get's a pointer to a char it thinks is a null-terminated string and therefore tries to read from the start of the memory until it finds the null-character? Is this correct and what's the proper way to solve this problem? Should i just pass the data by value as I'm pretty much doing?
Added:
Each node should hold just one char and nothing more, everything passed by char is just one char and nothing else. So i'm not having a string in chars so to say.
So it turns out what i was really interested in was how the streams handle char* input.
Thanks for the answers!
If something other is wrong in my code and you have the time please do tell me!
Thanks in advance!
new char(*node_name) allocates a single character corresponding to the first character of node_name. Not a string. When you use it like a NULL-terminated C string, the terminator byte is missing and you'll get garbage.
A char* is special for std::cout and other streams because it means "null-terminated C string". You'll have to dereference it (<< *_node_name <<) or, like hmjd suggests, simply use a single char (char node_name).
When you use node_name without reallocation, you run the risk that the space your pointer is referring to is already gone when you need it. You don't own the pointed-to memory, so you should either use it immediately or make a copy.
In general, writing own exception classes is not easy, and there is much to consider. In your case, just derive from std::logic_error or std::runtime_error, and let them handle the strings.
Why do you use pointers at all? Your node name is a single char, treat it as such.
class NoSuchNode : public std::exception
{
private:
char _node_name;
public:
NoSuchNode(char node_name) : _node_name(node_name) { }
virtual const char* what() const throw()
{
std::stringstream ss;
ss << "There exists no node for: " << _node_name << ".";
const std::string& tmp = ss.str();
return tmp.c_str();
}
};
(I’ve replaced the assignment in the constructor by an initialisation. This is a general best practice.)
It is not a good idea to return
return tmp.c_str();
because tmp.c_str() return a pointer to a memory location which will be freed after returning from the function.
When they are represented in memory, are C++ objects the same as C structs?
For example, with C, I could do something like this:
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);
}
I don't think C++ allows overloading the + operator for the built-in char * type.
So i'd like to create my own lightweight string class which has no extra overhead that std::string has. I think std::string is represented contiguously:
(int)length, (char[])data
I want exactly the same functionality but without prefixing the length (saving 8 bytes overhead).
Here is the code i'm using to test, but it results in a segfault
#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 << "'";
}
Edit: nobody really understands what i want to do. Yes i know i can have a pointer to the string in the class but thats 8 bytes more expensive than a plain cstring, i wanted exactly the same internal representation. Thanks for trying though
Edit: The end result of what i wanted to achieve was being able to use the + operator with NO extra memory usage compared to using strcat etc
const char * operator+(const char * first, const char * second);
You should not waste your time writing string classes - there's a reason people spent time writing them in the first place and it's naive to think they wrote them because they wanted to create big obfuscated and overheaded code that you could easily improve in a matter of hours.
For example your code has quadratic complexity for memory reallocations in the assignment operator - each assignment of a sting greater by 1 character will use a new memory block greater by 1 byte resulting in big memory fragmentation after a "few" assignments like this - you save a few bytes but potentialy lose megabytes to address space and memory page fragmentation.
Also designing this way you have no way of efficiently implementing the += operator as instead of just copying the appended string in most cases you will always need to copy the whole string - thus reaching quadratic complexity again in case you append small strings to one bigger one a few times.
Sorry but your idea looks to have great chances of becoming terrible to maintain and orders of magnitude less efficient then the typical string implementations like std::string.
Don't worry - this is true for practicaly all great ideas of "writing your own better version of a standard container" :)
struct myObj {
//...
char myVarChar;
};
This won't work. You either need a fixed size array, a pointer to char or use the struct hack. You won't be able to assign a pointer to this myVarChar.
so i'd like to create my own lightweight string class which has no extra overhead std::string has.
What extra overhead are you referring to? Have you tested and measured to see if std::string is really a bottleneck?
I think std::string is represented contiguously
Yes, mostly, the character buffer part. However, the following:
(int)length(char[])data
is not required by the standard. Translated: A string implementation need not use this particular layout of its data. And it may have additional data.
Now, your lightweight string class is frought with errors:
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?
};
Try something along the lines of the following:
/* 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;
};
Wow. What you're trying to do is a complete abuse of C++, would be totally compiler dependent if it worked, and would surely land you in TheDailyWTF some day.
The reason you're getting a segfault is probably because your operator= is reallocating the object to a different address, but you're not updating the myString pointer in main. I hesitate to even call it an object at this point, since no constructor was ever called.
I think what you're trying to do is make pString a smarter pointer to a string, but you're going about it all wrong. Let me take a crack at it.
#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() << "'";
}
Now I wouldn't use malloc but new/delete instead, but I left this as close to your original as possible.
You might think you are wasting the space of a pointer in your class, but you aren't - you're trading it for the pointer you previously kept in main. I hope this example makes it clear - the variables are the same size, and the amount of additional memory allocated by malloc/realloc is the same as well.
pString myString;
char * charString;
assert(sizeof(myString) == sizeof(charString));
P.S. I should point out that this code still needs a lot of work, it's full of holes. You need a constructor to initialize the pointer, and a destructor to free it when it's done, just for starters. You can do your own implementation of operator+, too.
You cannot change the size of an object/struct in either C or C++. Their sizes are fixed at compile time.
when they are represented in memory are objects C++ objects the same as C structs.
Strictly speaking, no. In general, yes. C++ classes and structs are identical in memory layout to C structs except:
Bit fields have different packing rules
Sizes are fixed at compile time
If there are any virtual functions, the compiler will add a vtable entry to the memory layout.
If the object inherits a base class, the new class' layout will be appended to the base class layout, including vtable, if any.
I don't think C++ allows overloading the + operator for the built in char * type. so i'd like to create my own lightweight string class which has no extra overhead std::string has. I think std::string is represented contiguously
You can create a operator+ overload for the char* type. Normal behavior is pointer arithmetic. std::string overloads operator+ to append char* data to the string. The string is stored in memory as a C string, plus additional information. The c_str() member function returns a pointer to the internal char array.
In your C example, you're relying on undefined behavior. Don't realloc like that. It can result in Bad Things - namely bizarre segfaults.
Your C++ example is also doing Bad Things in doing realloc(this). Instead, you should carry a char* and get a new char[] buffer to store the chars in instead of a realloc(). Behavior for such a realloc is undefined.
There is a lot a wrong with your class definition/usage. If you want to store a string you should use a pointer type, like char* a member, not an individual char. Using a single char means that only a single character of memory is allocated.
Another mistake is the allocation code where you do a realloc on this - you can potentially change the memory allocated, but not the value of this. You must assign the result to this to achieve this (this = (*pString)realloc(this, strlen(buff+1));) and that is really bad practice anyway. Much better to use realloc on a char* member.
Unfortunately C++ proper has no alternative for realloc or expand and you must instead use new and delete, doing any copying yourself.
Why do you write in C with classes, why don't use C++?
I do not think 'this' works the way you think it works.
Specifically, you cannot reallocate this to point to a larger buffer in a member function, because whatever called that member function still has a pointer to the old 'this'. Since it's not passed by reference there is no way that you can update it.
The obvious way around that is that your class should hold a pointer to the buffer and reallocate that. However, reimplementing a string class is a good way to give yourself lots of headaches down the line. A simple wrapper function would probably accomplish what you wanted (assuming "being able to use the + operator with NO extra memory usage compared to using strcat" is really what you wanted):
void concatenate(std::string& s, const char* c) {
s.reserve(s.size() + strlen(c));
s.append(c);
}
There's some probability that append may do that internally anyway though.
don't mind the lack of const correctness, as this is a mock up, but how about this:
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;
}
You are moving the "this" pointer. Thats not going to work.
I think what you really want is just a wrapper around a buffer.
#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.
#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 When realloc doesn't re-assign the pointer i.e.
Address of this: 1049008
Address of this->c: 1049008
Address of this After: 1049008
Address of myString: 1049008 'testing'
Works, but when the the following happens it fails
Address of this: 1049008
Address of this->c: 1049008
Address of this After: 1049024
Address of myString: 1049008 ''
the obvious solution is to have
this = (pString*) newPoint;
But the compiler complains about an invalid lvalue in assignment. Does anyone the correct way to update this (just for completeness, i doubt i'll use the code since everyone seems to hate it). Thanks
If you want something that is basically the same as std::string except that it doesn't know how long the string is, you should learn how std::string works, what operator overloads it has, etc. and then mimic that, with just the differences you want.
There is unlikely to be any real point to this, however.
Regarding your latest update - you say you want a design in which general application code will be passing around naked pointers to heap objects. With no automatic cleanup.
This is, quite simply, a very bad idea.
What you want to do doesn't and cannot work in C++. What you are looking for is the C99-feature of flexible arrays. This works nice in C99 for two reasons, first you don't have build-in constructors and second you don't have inheritance (at least not as a language feature). If a class inherits from another the memory used by the subclass is packed by hind the memory of the parent class, but a flexible array needs to be at the end the structure/class.
class pString {
char txt[];
}
class otherString : pString { // This cannot work because now the
size_t len; // the flexible array is not at the
} // end
Take std::string it was written by experts of C++, I'm sure they didn't leaved out a "good trick" without a reason. If you still find out that they don't perform very well in your programm, use plain C strings instead, of course, they don't provide the sweet API, you want.
You can't realloc C++ objects. As others pointed out this is not really a pointer you can modify, there's no guarantee that it will be pointing to an area realloc has access.
One solution to concatenation is to implement a class hierarchy that will defer the real concatenation until it is needed.
Something like this
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;
}
So if you have
MyString a = "hello";
MyString b = " world";
MyString c = a + b;
Will turn into
MyString c = MyConcatString(a, b);
For more detail check "The C++ Programming language".
Other solution, is to wrap char* inside a struct, but you seem to no like this idea.
But whatever solution you will choose, objects in C++ can't be relocated.
If you want performance, you can write your class like this:
template<int max_size> class MyString
{
public:
size_t size;
char contents[max_size];
public:
MyString(const char* data);
};
Initialize max_size to an appropriate value under context. In this way the object can be created on stack, and no memory allocation is involved.
It is possible to create what you desired by overloading new operator:
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;
This code is a mess and RnR and others suggested is not advisable. But it works for what i want it to do:
#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);
}