This question already has answers here:
What is a dangling pointer?
(7 answers)
Is returning a reference to a local int variable always undefined behavior
(1 answer)
What are the basic rules and idioms for operator overloading?
(8 answers)
Why is the phrase: "undefined behavior means the compiler can do anything it wants" true?
(2 answers)
Closed last month.
I wrote my own string class for learning purposes. I'm trying to overload the += operator so that I can append strings together. But whenever I use the operator the inital object remains the same. I am confused.
StringF& StringF::operator+=(StringF& obj) {
const char* string = this->getString();
const char* stringToAppend = obj.getString();
const int stringLength = this->length();
const int stringToAppendLength = obj.length();
char* appendedString = new char[stringLength + stringToAppendLength];
appendedString[stringLength + stringToAppendLength] = '\0';
for (int i = 0; i < stringLength; i++) {
appendedString[i] = string[i];
}
for (int i = stringLength; i < stringLength + stringToAppendLength; i++) {
appendedString[i] = stringToAppend[i - stringLength];
}
StringF appendedObj = StringF::StringF(appendedString);
std::cout << "Appended obj: " << appendedObj.getString() << std::endl; //For debugging
return appendedObj;
}
Here is the class header file:
class StringF {
private:
const char* string;
public:
StringF(const char*);
int length();
const char* copy();
const char* getString();
const char* reverse();
int find(const char*);
StringF& operator+=(StringF&);
friend std::ostream& operator<<(std::ostream&, StringF&);
};
I want to be able to create two strings and append with one another. But the it doesn't work. Any ideas? If possible, I'd want an explanation as to why this doesn't work so I can try to figure it out on my own, as I am trying to learn I don't want the solution handed to me. But I'll take whatever.
So here is what I'm trying to do.
StringF s("Hello");
StringF s2(", World!");
s += s2;
std::cout << s << std::endl; //Should print "Hello, World!" but only prints "Hello".
Thanks in advance!
Edit: Here is the example for you to try. https://onlinegdb.com/ApfkIONXG
There are a few issues with the code as posted:
There is a buffer overflow
You are not modifying the this instance like operator+= is supposed to
You are returning a reference to a local
Here is some minimally edited code that fixes these issues
StringF& StringF::operator+=(StringF& obj) {
const char* string = this->getString();
const char* stringToAppend = obj.getString();
const int stringLength = this->length();
const int stringToAppendLength = obj.length();
// +1 for the null character we are appending
char* appendedString = new char[stringLength + stringToAppendLength + 1];
appendedString[stringLength + stringToAppendLength] = '\0';
for (int i = 0; i < stringLength; i++) {
appendedString[i] = string[i];
}
for (int i = stringLength; i < stringLength + stringToAppendLength; i++) {
appendedString[i] = stringToAppend[i - stringLength];
}
// TODO: de-allocate the previous 'string' member variable
// Now we need to update `this`. While we can't modify the data pointed
// at by the existing member pointer, we can change the pointer to point
// to the newly allocated data:
string = appendedString;
// operator+= should be returning a reference to `this`
return *this;
}
We can modify the member variable because const char* is a mutable pointer to constant characters. This means the characters at the address of the pointer are constant, but the memory storing the pointer address is still mutable.
|string: 0xDEADBEEF| // The pointer value is not const
|
|----> |abc\0| // This data is const
see Const before or const after? for more info on the const issue.
The return *this is because the general expectation of operator+= is that the following code should work:
StringF s1("a");
StringF s2("b");
StringF s3 = (s += s2);
assert(s3 == s);
This might seem like non-sense code in this simple example, but this pattern is useful in some cases and is the expected behavior of a operator+= overload.
Related
At first, I would like to point out that despite using C ++ I cannot use strings or vectors. It is like C with objects.
Ok I have class A with char* test() method:
char* A::test()
{
char to_return[3*this->some_value+3];
for (int i = 0; i < this->some_value; i++)
{
to_return[3*i] = '♥';
to_return[3*i+1] = 'b';
to_return[3*i+2] = ' ';
}
char* dto_return = to_return;
return std::move(dto_return);
}
next in object of class B I have:
ofstream file;
file.open("myfile.txt", ofstream::out | ofstream::trunc);
file.close();
file.open("myfile.txt");
char* to_write = a_obj->test();
size_t len = strlen(to_write);
file.write((char*)&len, sizeof(len));
file.write(to_write, len);
file.close();
(based on this answer)
but the content of the file is:
¥b ¥b ¥b m
and it is definitely not what I'm looking for. The content should be:
♥b ♥b ♥b
How to fix that?
The problems are:
to_return is a local array that ends its lifetime on returning from the function, so returning its pointer is a bad idea.
'♥' may differ from what you want, especially when ♥ cannot be represented by one byte in your character code.
To overcome this problems:
Allocate a dynamic array that persists after returning from the function.
Use string literal to represent the characters to add.
char* A::test()
{
const char *part = "♥b ";
size_t part_len = strlen(part);
char *to_return = new char[part_len*this->some_value+1];
for (int i = 0; i < this->some_value; i++)
{
strcpy(to_return + part_len * i, part);
}
return to_return;
}
The dynamic array returned should be freed via delete[] after completed to use.
char* to_write = a_obj->test();
// ...
delete[] to_write;
I'm currently implementing my own string class (just for training), but I'm experiencing some problems in my substr() method:
MyString MyString::substr(size_t position, size_t length)
{
if (checkBounds() || length == 0)
{
return MyString();
}
char* tmp = new char[length + 1];
memcpy(tmp, this->s + position, length);
tmp[length] = STRING_ESCAPE;
MyString result(tmp);
delete[] tmp;
tmp = nullptr;
return result;
}
When I call this method and print the return value (I'm printing the char array actuallay, not the object itself), I receive complete garbage, which is carried out as a bunch of squares.
But when I return a temporary object return MyString(tmp), everything works fine. Initially i suspected this issue is associated to the delete[] operation, but commenting it out shows no effect.
The MyString constructor which is called is the following:
MyString::MyString(const char* s)
{
size_t length = this->strlen(s);
this->sLength = length;
this->s = new char[length + 1];
for (size_t i = 0; i <= length; ++i)
{
this->s[i] = *s;
++s;
}
}
So where is my mistake? Thank you!
In the two places you create MyString, they are being created within the context of your substr function. The return statement has to make a copy of them. Your copy constructor is probably not doing what you would need it to do.
A simpler design is for your substr function to return a pointer to a MyString that you create with operator new.
You were indeed missing the copy constructor. Something like this would do the work:
MyString::MyString(const MyString& other) {
size_t length = strlen(other.s);
s = new char[length+1];
strcpy(s,other.s);
this->sLength = length;
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I prefer char* instead std::string, so I wrote a function combining char pointers. I created a new project and tested it. It works fine. But when I try to use it in some larger GUI project, my program crashes.
Here's an example (working) code:
#include <Windows.h>
#include <vector>
char *StringJoin(const char *String, const char* ...)
{
va_list ArgList;
va_start(ArgList, String);
std::vector<const char *> StringData;
std::vector<unsigned int> StringLen;
StringData.push_back(String);
unsigned int SingleLength = strlen(String);
StringLen.push_back(SingleLength);
unsigned int TotalLength = SingleLength;
while (1)
{
const char* Val = va_arg(ArgList, const char*);
if (!Val)
break;
StringData.push_back(Val);
SingleLength = strlen(Val);
StringLen.push_back(SingleLength); // In larger projects it crashes here
TotalLength += SingleLength;
}
va_end(ArgList);
char *NewString = new char[TotalLength + 1];
unsigned int VectorSize = StringData.size();
unsigned int NewLength = 0;
for (unsigned int Element = 0; Element < VectorSize; Element++)
{
memcpy(NewString + NewLength, StringData[Element], StringLen[Element]);
NewLength += StringLen[Element];
}
NewString[TotalLength] = '\0';
StringData.clear();
StringLen.clear();
return NewString;
}
int main(void)
{
char* New = StringJoin("Does ", "it ", "works ", "for ", "you ", "?");
printf("%s\n", New);
system("PAUSE");
return 1;
}
Is my code safe and stable?
You can't use the !Val condition:
const char* Val = va_arg(ArgList, const char*);
if (!Val)
break;
When using variable argument list you need to know exactly how many arguments have been passed, or (update) when to stop processing the arguments, using e.g. NULL / nullptr (or any other) sentinel.
You definitely should use variadic templates if your compiler supports it.
The following quite simple function takes an arbitrary number of strings and concatenates it in a loop. It supports both std::string and char* for the arguments (and even mixed):
template<class ...Strings>
std::string StringJoin(Strings ...strings) {
std::string joined;
for (auto s : { strings... })
joined += s;
return joined;
}
Live Demo
Now if you want the result to be a char*, you need to think about where you allocate the memory and where you deallocate it, i.e. you need to manually manage memory. Unless you use smart pointers:
template<class ...Strings>
std::unique_ptr<char[]> StringJoin(Strings ...strings) {
std::string joined;
for (auto s : { strings... })
joined += s;
std::unique_ptr<char[]> buf(new char[joined.size() + 1]);
memcpy(buf.get(), joined.data(), joined.size());
buf[joined.size()] = '\0';
return buf;
}
Live Demo
A little context: I'm trying to make a very simple hashing function/hash table as described here. I'm basically on the first step, blindly adding a key to an array based on the letter it starts with (no checking if space is occupied yet). The code I'm using to do this so far:
int main(int argc, char **argv) {
char *arrayKeys[300];
std::string aName("Charles");
char *aNameCpy = new char[aName.size() + 1];
std::copy(aName.begin(), aName.end(), aNameCpy);
aNameCpy[aName.size()] = '\0';
int kPos = storeKey(arrayKeys, aNameCpy);
std::cout << "The new position in arrayKeys for 'Charles' is: " <<
kPos << "\ncontaining the text: " << arrayKeys[kPos] << std::endl;
delete[] aNameCpy;
return 0;
}
int storeKey(char **keys, char *key) {
int charLett = -1;
charLett = (int)key[0];
if(charLett != -1)
charLett = charLett - 65;
keys[charLett * 10] = key;
return charLett*10;
}
My question is, how can I add a string to the array (arrayKeys) where it is fully apart of the array, and not reliant upon the original string? If I delete the copy of the string (aNamCpy) before I print the array key out, the array key turns into garbled symbols. I'm copying the string before sending it to the function because I need a non-const string to add to the arrayKeys array (so it can be modified), and any method of string I looked at seemed to return const.
(Another version of this I attempted can be found here, but I would rather not initialize the arrayKeys like that - with a definite second dimension (string) length)
C++ is still very new to me so I can't figure out how to juggle the non-const part with copying the string into arrayKeys. Any help would be very much appreciated.
Here's how I'd change the code to use more modern C++ constructs. I think you'll find this way easier to use.
int storeKey(vector<string> &keys, const string &key) {
int charLett = -1;
if (!key.empty()) { // you weren't doing this before!
charLett = key[0];
charLett = toupper(charLett) - 'A';
keys[charLett * 10] = key;
}
return charLett*10;
}
int main() {
vector<string> arrayKeys(300);
std::string aName("Charles");
// No need to bother with the awkward copying.
// std::vector and std::string will take care of it for us.
int kPos = storeKey(arrayKeys, aName);
if (kPos >= 0) {
cout << "The new position in arrayKeys for 'Charles' is: " <<
kPos << "\ncontaining the text: " << arrayKeys[kPos] << endl;
}
// Don't have to remember to delete anything because nothing was new'ed.
return 0;
}
(#Kristo has the right idea. I'm just going to add a comment to the question as asked.)
Basically, don't delete aNameCpy. You require the copy to remind valid and therefore it shouldn't be deleted. You should only delete the strings if and when you ever delete the entire hash.
C++ is still very new to me so I can't figure out how to juggle the
non-const part
You could declare both keys and key to be const char **keys, const char *key. keys is pointer-to-pointer-to-char. More precisely, it's a pointer to nonconst pointer to const char. In other words, you can modify keys, you just cannot modify the actual characters it points (indirectly) at.
So, simply put const in your declaration of storeKey int storeKey(const char **keys, const char *key) and update arrayKeys accordingly const char *arrayKeys[300];
One final style issue: You should copy the string inside storeKey, not in main. This is better design, as it makes clear to the reader that storeKey "owns" the copy.
int storeKey(char **keys, const char *key) {
char * the_copy = new char[strlen(key)+1];
strcpy(the_copy, key);
... and so on
But, in short, use C++ string instead of all this, if you can!
i had met same situation in my project.
The following code were similarly reproduced.
#include <iostream>
#include <vector>
using namespace std;
class METHOD
{
public:
vector<METHOD*> m_arg;
string name;
string getName()
{
return name;
}
METHOD()
{
}
METHOD(string n)
{
name = n;
}
};
class PF : public METHOD
{
public:
PF(){};
};
void main()
{
PF p;
p.m_arg.push_back(new METHOD(string("a")));
p.m_arg.push_back(new METHOD(string("b")));
p.m_arg.push_back(new METHOD(string("c")));
for(int i = 0 ; i < (int)p.m_arg.size() ; ++i)
{
const char* ccs = p.m_arg[i]->getName().c_str(); //PROBLEM POINT
size_t ss = strlen(ccs);
}
}
the problem is //PROBLEM POINT
the return address of c_str() can't be assigned to const char* ccs.
in debugger, the css assigned memory address of return of c_str(), but css's value is "" and it work same as ""
but the next code is working as expected.
string str("ok");
const char* ckk = str.c_str();
after this code, ckk points after str.c_str() returnning addres, and ckk's value in debugger is "ok"
the same problem is occuring in std::wstring, wchar_t, unicode.
why did this problem occur?
who knows? help me plz~
ADD:
thanks to mystical i solve the problem same way. but this solution also work OK.
for(int i = 0 ; i < (int)p.m_arg.size() ; ++i)
{
string& strr = p.m_arg[i]->getName();
const char* ccs = strr.c_str();
size_t ss = strlen(ccs);
}
so i think the return value of getName() is live until the loop } closed.
but interestingly, the next is don't work
for(int i = 0 ; i < (int)p.m_arg.size() ; ++i)
{
string* strr = &p.m_arg[i]->getName();
const char* ccs = strr->c_str();
size_t ss = strlen(ccs);
}
the css is 'dangling' and also strr is ""
i've dazed and confused!
In your getName() function:
string getName()
{
return name;
}
The string that is returned is a copy of the original. It becomes an intermediate in the following expression:
const char* ccs = p.m_arg[i]->getName().c_str();
When the expression ends, that intermediate is destroyed. Therefore invalidating the pointer returned by c_str(). The result is that ccs becomes a dangling pointer.
The reason why this works:
string str("ok");
const char* ckk = str.c_str();
is because str is not an intermediate.
To make it work, change your loop to this:
for(int i = 0 ; i < (int)p.m_arg.size() ; ++i)
{
string str = p.m_arg[i]->getName()
const char* ccs = str.c_str();
size_t ss = strlen(ccs);
}
EDIT : To answer your new question:
The reason why this doesn't work:
string* strr = &p.m_arg[i]->getName();
is the same as before. getName() returns an intermediate. You then take the address of this and assign it to strr. The intermediate is destroyed after the statement, so strr becomes a dangling pointer as in the first case.