I'm doing an exercise right now, so I will say right away that I don't want to use std::string to solve my problem.
I have a simple class that has a few fields and among them is char* text field that can be assigned and re-written or re-allocated.
class MyClass
{
private:
char* name;
...
public:
operator char*() const { return name;}
}
What I'm trying to do is make an implicit conversion to char* so that I can do this in my program:
Myclass s = "Hello" //allocates 6 bytes and pust a 0-terminated "Hello/0" line into "name"
char* c = s
s = "Hello World" //re-allocates memory, frees previous memory
cout << "Str: " << s << " Char: " << c;
Obviously, just as I noticed even before testing, when the re-allocation of "name" happens and the memory is freed - "c" now points to garbage.
So this code is no-go and I fully understand why.
I could allocate new memory and return a pointer to it, I guess:
operator char*() const
{
int length = strlen(name)+1;
char* ptr = new char[length]
std::copy(name, name+length, ptr);
return ptr;
}
but in this case, if I later re-assign "c" to something else - then the memory will never be freed, right? If I do this:
c = "Bye";
then basically the memory that I copied "name" to will still remain allocated and leaK?
The question is: how to make this right? Should I even worry about this, or do I simply assume that the user who re-assigns "c" should take care of clearing the memory first?
I will repeat that my goal is to do this through memory manipulation and pointers rather than safely use std::string.
Thank you!
Related
I'm just looking at the following code snippet:
#include<iostream>
#include<cstring>
using namespace std;
class String
{
private:
char *s;
int size;
public:
String(const char *str = NULL); // constructor
~String() { delete [] s; }// destructor
void print() { cout << s << endl; }
void change(const char *); // Function to change
};
String::String(const char *str)
{
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}
void String::change(const char *str)
{
delete [] s;
size = strlen(str);
s = s + 1;
s = new char[size+1];
strcpy(s, str);
}
int main()
{
String str1("StackOverFlow");
String str2 = str1;
str1.print();
str2.print();
str2.change("StackOverFlowChanged");
str1.print();
str2.print();
return 0;
}
I expect the output as:
StackOverflow,
StackOverflow,
StackOverflow,
StackOverflowChanged.
Before str2.change("StackOverFlowChanged") line, both str1 and str2's s point to the same memory location. However, in change method, since the pointer value changed, I except now str1 and str2's s point to the different locations and this is not the case. Can somebody explain why this is the case?
After calling str2.change, str1 no longer has a valid pointer. Since the two objects were sharing the same pointer, by deleting the array in one object, the other object now points to a deleted array. Attempting to access the array pointed to by str1 is undefined behavior, so str1.print() is invalid code.
Now, in this particular case, odds are good that the new char[] in str2.change just so happens to return a pointer to an address identical to the one that was just deleted. After all, the memory was just freed up, and no other allocations were made in the meantime. So while str1's pointer is still invalid, it just so happens to work out to pointing at a valid string by the time str1.print gets called.
But that's just happenstance; an implementation didn't have to do that. Undefined behavior is undefined, and you need to properly follow the Rule of 5. Or just use std::string.
The problem is that you called delete[] on the memory pointed by str1.
The runtime library is then allowed to reuse the same memory for the next allocation call and apparently this is what happened.
str1 didn't change the memory it pointed to, but the content changed.
Note that using a pointer to memory after it has been delete[]d is undefined behavior. So indeed anything can happen.
When you write classes that own heap allocated memory a lot of care should be taken about who owns the memory and when this memory is going to be released; in particular a lot of attention should go to copy constructor and assignment operator because the automatically generated code is rarely the correct one in case of memory owned using raw pointers.
No, str1 and str2 do not point to the same memory location, before str2.change("StackOverFlowChanged") line. str2 is in a different location but with the same value as of str1. You can verify this using -
cout << &str1 << '\t' << &str2 << endl;
It will show that they have different locations in memory.
I'm looking for a way to associate a char array with a string so that whenever the char array changes, the string also changes. I tried to put both char array and string variables in a union but that didn't worked as the compiler complained...
Any ideas are welcome...
class Observable_CharArray
{
char* arr;
std::function<void(char*)> change_callback;
public:
Observable_CharArray(int size, std::function<void(char*)> callback)
: arr(new char[size]), change_callback(callback){}
~Observable_CharArray()/*as mentioned by Hulk*/
{
delete[] arr;
}
void SetCallback(std::function<void(char*)> callback)
{
change_callback = callback;
}
/*other member function to give access to array*/
void change_function()
{
//change the array here
change_callback(arr);
}
};
class Observer_String
{
std::string rep;
void callback(char* cc)
{
rep = std::string(cc);
}
public:
Observer_String(Observable_CharArray* och)
{
och->SetCallback(std::bind(&callback, this, _1));
}
/*other member functions to access rep*/
};
The design can definitely be improved.
There can be other ways to solve your actual problem rather than observing char arrays.
The problem is that the std::string may change the string array inside (especially when it resizes). For instance, c_str returns the address of the current string - documentation says that "The pointer returned may be invalidated by further calls to other member functions that modify the object.".
If you're sure you won't call string methods (hence the string will stay at the same memory location), you could try accessing the c_str pointer (your char array) directly and modify its content.
std::string str = "test";
char* arr = (char*)str.c_str();
arr[3] = 'a';
NOTE: I strongly advice against this unless in a testing context.
In other words, the string class doesn't guarantee it's going to stay in the same place in memory - meaning trying to access it through a char array is impossible.
The best is to create another string class that enforces the char array to always stay the same size (and so can stay in the same memory position all the time). You could also create a bigger array (max size string for instance) to cope with any string size changes - but that should be enforced in your wrapper class.
Well you can do this, but you shouldn't
#include <iostream>
#include <string>
int main()
{
std::string test("123456789");
std::cout << test << "\n";
char* data = &test.front(); // use &(*test.begin()) for pre-C++11 code
for ( size_t i(0); i < test.size(); ++i )
{
data[i] = 57 - i;
}
std::cout << test << "\n";
}
Output will be
123456789
987654321
This however goes again everything std::string is trying to facilitate for you. If you use data, you risk causing UB and changes to test may make data point to garbage.
You should not do this!
However, there are many (dangerous) ways to achieve it:
char* cStr = const_cast<char*>(cppStr.c_str());
or
char* cStr = const_cast<char*>(cppStr.data());
or
char* cStr = &cppStr[0];
But beware that the cppStr might be reallocated whenever you touch it, hence invalidating your cStr. That would crash at some point in time, although maybe not immediately (which is even worse).
Therefore, if you are going to do this anyway. Make sure to cppStr.reserve(SOMETHING) *before* you get the cStr out of it. This way, you will at least stabilise the pointer for a while.
This is in reference to the discussion in this topic here
How to have a char pointer as an out parameter for C++ function
In the code below, where do I free the memory of pTemp? Is it not required?
Would things would have changed in anyway if instead of array of chars there was array of integers?
void SetName( char **pszStr )
{
char* pTemp = new char[10];
strcpy(pTemp,"Mark");
*pszStr = pTemp;
}
int main(int argc, char* argv[])
{
char* pszName = NULL;
SetName( &pszName );
cout<<"Name - "<< pszName << endl;
delete [] pszName;
cin.get();
return 0;
}
You're assigning pTemp to *pszStr, and pszStr points to pszName.
Therefore, delete [] pzxName deletes the allocated memory as required.
The confusion is a good example of why you should avoid weird C-style idioms and manual memory management. It would be much clearer as:
#include <string>
#include <iostream>
std::string GetName() {return "Mark";}
int main() {
std::string name = GetName();
std::cout << "Name - " << name << std::endl;
}
with no pointers, new or delete required.
You don't need to.
The memory allocated to it is deallocated when you call:
delete [] pszName;
Note, that new/new [] returns you a address and to avoid a memory leak you need to call delete/delete [] on the exact same address and only once.
What you have in above code is two different pointers(pszName and pTemp) pointing to the same address which was returned by new []. So calling delete [] on one suffices.
In C++ you are much better off using a std::string instead of char * and in case of other data types, You surely can use smart pointers and save yourself the explicit memory management.
pTemp, the variable itself, is deallocated once the function exits since it was allocated on the stack in the first place. The array whose pointer was put into pTemp persists outside the function via the argument passed to it, pszName. When the value in this variable is deleted, the heap memory used by the array will be freed.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
C++ deprecated conversion from string constant to 'char*'
I want to pass a string via char* to a function.
char *Type = new char[10];
Type = "Access"; // ERROR
However I get this error:
error: deprecated conversion from string constant to 'char*'
How can I fix that?
If you really want to modify Type:
char *Type = new char[10];
strcpy( Type, "Access" );
If you don't want to modify access:
const char *Type = "Access";
Please note, that, however, arrays of char in C and in C++ come with a lot of problems. For example, you don't really know if the call to new has been successful, or whether it is going to throw an exception. Also, strcpy() could surpass the limit of 10 chars.
So you can consider, if you want to modify type later:
std::string Type = "Access";
And if you don't want to modify it:
const std::string Type = "Access";
... the benefit of using std::string is that it is able to cope with all these issues.
There are a couple of things going on here.
char *Type = new char[10];
This create a char* pointer named Type and initializes it to point to the first element of a newly allocated 10-element array.
Type = "Access"; // ERROR
This assignment doesn't do what you think it does. It doesn't copy the 6-character string "Access" (7 characters including the terminating '\0') to the array you just created. Instead, it assigns a pointer to the first element of that array into your pointer Type. There are two problems with that.
First, it clobbers the previous value of Type. That 10-character array you just allocated now has nothing pointing to it; you can no longer access it or even deallocate it. This is a memory leak.
This isn't what the compiler is complaining about.
Second, a string literal creates a statically allocated const array ("statically allocated" meaning it exists for the entire execution of your program). Type is not declared with a const qualifier. If the compiler allowed you to point Type to the string "Access", you could use that pointer to (attempt to) modify it:
Type = "Access";
Type[0] = 'a'; // try to change the string to "access"
The purpose of const is to prevent you from modifying, or even attempting to modify, things that are read-only. That's why you're not allowed to assign a non-const pointer value to a const pointer object.
Since you're programming in C++, you're probably better off using std::string.
I want to pass a string via char* to a function.
Here is how you can pass a string via char* to a function (note the required const keyword in the function signature.)
#include <iostream>
void f(const char* p) {
std::cout << p << "\n";
}
int main() {
f("Access");
}
But, what if you are invoking an existing function, and cannot modify its signature?
If you have some external guarantee that the function will not write through its argument pointer,
#include <iostream>
void f(char* p) {
std::cout << p << "\n";
}
int main() {
f(const_cast<char*>("Access"));
}
If, on the other hand, the function might write to the string, then you'll need to allocate space for the string:
#include <iostream>
void f(char* p) {
*++p;
std::cout << p << "\n";
}
int main() {
// Allocate read-write space on the heap
char *p = new char[strlen("Access"+1)];
// Copy string to allocated space
strcpy(p, "Access");
f(p);
delete p;
}
or,
#include <iostream>
void f(char* p) {
*++p;
std::cout << p << "\n";
}
int main() {
// Allocate read-write space on the stack
char arr[] = "Access";
f(arr);
}
But, the best course by far is to avoid the whole pointer mishegas:
#include <iostream>
void f(const std::string& p) {
std::cout << p << "\n";
}
int main() {
f("Access");
}
You've got a basic operations problem here, not a coding issue.
When you want to change the contents of a C char array, you do not use the assignment operator. That will instead change the value of the underlying pointer. Ick.
Instead you are supposed to use the C string library routines. For instance, strcpy (Type, "Access"); will copy the string literal "Access" into your character array, with its all-important trailing nul character.
If you are using C++ (as your tags indicate), you should probably be using std::string instead of arrays of char. Assignment works they way you are expecting there.
I have a member function of a class which is defined below, say
int x(std::string &a, std::string &b) {
char *ptr = another_member.getStringMember().c_str() //I am storing the pointer
cout << ptr << endl;
a="hello";
cout << ptr << endl;
}
The output is
StringMember
Hello
Can you please explain why this happens ?? Thanks
Most likely because another_member.getStringMember and a are the same string.
In this case it is not actually legal to use ptr after the string has been modified with a="hello"; because mutating operations can make the previously obtained pointer invalid.
Just out of curiosity, do you call
x(another_member.getStringMember, fooBar);
?
c_str() returns internal pointer of string object which became invalid as soon as you modify the source string
You are not guaranteed that ptr is still usable after the a="hello" line (since it looks like they are the same string). In your case, since Hello was smaller, and the string wasn't being shared, it looks like it reused the space.
This is implementation specific behavior. It could have easily crashed.
The temp std::string from another_member.getStringElement() goes out of scope after the line is executed. Change
char *ptr = another_member.getStringMember().c_str();
to
std::string s = another_member.getStringMember();
const char *ptr = s.c_str();
Did you mean to do another_member.getStringElement().c_str() as against another_member.getStringElement.c_str().