Binding a pointer temporary to a reference
Please refer to the code below. In this code I am storing the const char*
returned by test.c_str()
into a reference. My question is - Will the data
be correctly refering to the contents of test
? I am thinking that the ptr returned by test.c_str()
will be a temporary and if I bind it to a reference that reference will not be valid.
Is my thinking correct?
class RefPtrTest
{
std::string test;
StoringClass storingClass;
public:
RefPtrTest(): test("hello"), storingClass(test.c_str())
{
}
}
where StoringClass is
class StoringClass
{
const char*& data;
public:
StoringClass (const char*& input): data(input)
{
}
}
EDIT1:
Let's just not consider what std::string is doing. Suppose I am using my own class called mystring
class RefPtrTest
{
const mystring test;
StoringClass storingClass;
public:
RefPtrTest(): test("hello"), storingClass(test.getInternalPointer())
{
}
}
getInternalPointer
directly returns the internal pointer. I want to verify this hypothesis, while storingClass(test.getInternalPointer())
ptr returned by test.getInternalPointer()
will be a temporary and if I bin开发者_StackOverflow中文版d it to a reference that reference will not be valid. Is my thinking correct?
EDIT2:
That StoringClass is not under my control. Basically it's a template class where it stores reference to the type. I am using it for const char*
. I am aware of all the design issues which you have raised. But I can't change that class, and I have to use it for const char *
. there is no other way around it.
The standard has the following to say about c_str
(21.3.6/2)
Requires: The program shall not alter any of the values stored in the array. Nor shall the program treat the returned value as a valid pointer value after any subsequent call to a non-const member function of the class basic_string that designates the same object as this.
So the answer is no, you can not treat the pointer as a reference to the contents of the string (after any non const function calls on the string).
Yes, your thinking is correct (test.c_str()
returns a temp, so you can't use it to initialize a reference), unless test.c_str()
actually returns a reference to a pointer, which I don't think it does... does it?
This should give you a compile error, though, did you try it?
In this particular case, actually using the pointer for something would not make much sense. But if you're only asking about references to pointers, then you're correct (regardless of type).
If StoringClass needs a reference to a pointer, then you have to ensure that there is a pointer for it to refer to (and modify, since it's not a const reference), with a lifetime as long as that of the reference.:
#include <string>
#include <iostream>
template <typename T>
class StoringClass {
T& data;
public:
StoringClass (T& input): data(input) { }
void print() const { std::cout << data << "\n"; }
void set(T x) { data = x; }
};
class RefPtrTest {
const std::string test;
const char *ptr;
StoringClass<const char*> storingClass;
public:
RefPtrTest(): test("hello"), ptr(test.c_str()), storingClass(ptr) { }
void print() const { storingClass.print(); }
void set(const char* x) { storingClass.set(x); }
};
int main() {
RefPtrTest t;
t.print();
t.set("world");
t.print();
}
Output:
hello
world
IMO StoringClass is a bit weird. For instance, I could have marked the set
functions const
too, and it'd still work. But if you have to use it, you have to use it. If you can use a StoringClass<const char *const>
instead, that might be better: it will ensure that you don't call any functions of StoringClass which modify the pointer. The way you asked the question suggests that you don't expect that to happen.
Edit:
If test
wasn't const, though, RefPtrTest could have a function:
void set_another_way(const char *x) { test = x; set(test.c_str()); }
Which would modify the string and update the pointer to which storingClass
refers. The function body is equivalent to test = x; ptr = test.c_str();
. Assuming single-threaded code, that also addresses any concerns about the validity of the pointer value returned by test.c_str()
, provided that set_another_way
is the only means by which test
is ever modified.
Frankly, StoringClass
shouldn't be called StoringClass
. It doesn't store anything ;-p
You should not modify the string buffer directly if that's what you're trying to do.
When you're dealing with pointers in general, you only need a reference to a pointer if you want to change the address the pointer type holds. If you only want to change the buffer then you can simply store a char*.
If you want to access a buffer like this use std::vector instead with something like this:
std::vector<char> v;
v.resize(size);
strcpy(&v.front(), "testing");
See the following comment about the std::string c_str() member function from cplusplus.com:
The returned array points to an internal location with the required storage space for this sequence of characters plus its terminating null-character, but the values in this array should not be modified in the program and are only granted to remain unchanged until the next call to a non-constant member function of the string object.
Although you don't call any non-const member functions in the code that you posted, this is not a very good idea. When you modify the private std::sting test variable in the RefPtrTest class, you inadvertently affect the StoringClass class (this violates encapsulation of RefPtrTest). Somebody maintaining your code later (maybe you) may not realize this and introduce a bug/crash.
It may be valid. You should not depend on this behaviour, however.
The string
class implementation has a char*
underneath and c_str()
method likely just returns the pointer to the beginning of that array. However, as the string changes over time, this internal array may be moved in the memory, resized, whatsoever. You should invoke c_str()
every time you'd like to convert string
to char*
.
It likely just returns the pointer to internal memory, so is very fast anyway.
精彩评论