2010-04-01 2 views
4

См. Приведенный ниже код. В этом коде я сохраняю const char*, возвращенный test.c_str() в ссылку. Мой вопрос: правильно ли будет data со ссылкой на содержимое test? Я думаю, что ptr, возвращаемый test.c_str(), будет временным, и если я привяжу его к ссылке, ссылка будет недействительной.Привязка указателя к ссылке

Я считаю, что это правильно?

class RefPtrTest 
{ 
    std::string test; 
    StoringClass storingClass; 
public: 
    RefPtrTest(): test("hello"), storingClass(test.c_str()) 
    { 
    } 
} 

где StoringClass является

class StoringClass 
{ 
    const char*& data; 
public: 
    StoringClass (const char*& input): data(input) 
    { 
    } 
} 

EDIT1: Давайте просто не считаю, что StD :: строка делает. Предположим, я использую свой собственный класс под названием MyString

class RefPtrTest 
{ 
    const mystring test; 
    StoringClass storingClass; 
public: 
    RefPtrTest(): test("hello"), storingClass(test.getInternalPointer()) 
    { 
    } 
} 

getInternalPointer непосредственно возвращает внутренний указатель. Я хочу проверить эту гипотезу, а storingClass(test.getInternalPointer()) ptr, возвращаемый test.getInternalPointer(), будет временным, и если я привяжу его к ссылке, эта ссылка будет неверной. Правильно ли я считаю?

EDIT2: Этот StoringClass не под моим контролем. В основном это класс шаблонов, где он хранит ссылку на тип. Я использую его для const char*. Я знаю все проблемы дизайна, которые вы подняли. Но я не могу изменить этот класс, и я должен использовать его для const char *. Другого пути нет.

+0

Я не понимаю, почему вы хотите сохранить ссылку на указатель вместо копии указателя. – pmr

ответ

1

Если StoringClass нуждается в ссылке на указатель, вы должны убедиться, что есть указатель на него, чтобы ссылаться (и изменять, поскольку это не ссылка на константу), с продолжительностью жизни до тех пор, пока ссылка .:

#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(); 
} 

Выход:

hello 
world 

IMO StoringClass немного странно. Например, я мог бы также отметить функции setconst, и он все равно будет работать. Но если вам нужно использовать его, вы должны его использовать. Если вы можете использовать StoringClass<const char *const>, это может быть лучше: это гарантирует, что вы не вызываете никаких функций StoringClass, которые изменяют указатель. То, как вы задали вопрос, говорит о том, что вы не ожидаете, что это произойдет.

Edit:

Если test не константные, хотя, RefPtrTest может иметь функцию:

void set_another_way(const char *x) { test = x; set(test.c_str()); } 

Что бы изменить строку и обновить указатель на который ссылается storingClass. Тело функции эквивалентно test = x; ptr = test.c_str();. Предполагая однопоточный код, который также учитывает любые сомнения относительно правильности значения указателя, возвращаемого test.c_str(), при условии, что set_another_way является единственным средством, с помощью которого изменяется test.

Честно говоря, StoringClass не следует называть StoringClass. Он ничего не хранит; -p

+0

Я сделал то же самое (принимая еще один 'const char *'), чтобы исправить эту проблему, поскольку это дало мне семантику RAII. –

3

Да, ваше мышление верное (test.c_str() возвращает temp, поэтому вы не можете использовать его для инициализации ссылки), если только test.c_str() не возвращает ссылку на указатель, который я не думаю, что он делает ... Имеет ли это?

Это должно дать вам ошибку компиляции, однако, вы попробовали?

В этом конкретном случае, фактически использование указателя для чего-то не имеет большого смысла. Но если вы только спрашиваете о ссылках на указатели, то вы правы (независимо от типа).

+1

test.c_str() возвращает указатель, test - это 'std :: string' –

+0

Я просто не знал, будет ли std :: string :: c_str() возвращать ссылку на указатель. – falstro

+0

Директивы @roe 'const' для временного доступа являются законными, см. Следующее: http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry (хотя использование данного примера в данном конкретном случае бессмысленно). –

3

Стандарт имеет следующие сказать о c_str (21.3.6/2)

Требуется: Программа не изменяет любое из значений, хранящихся в массиве. не несет программу лечения возвращаемого значения в качестве действительного значения указателя после любого последующего вызова функции неконстантной член класса basic_string, обозначающих того же объекта, как это.

Таким образом, ответ отрицательный, вы не можете обращаться с указателем как ссылку на содержимое строки (после вызова любой функции non const в строке).

+0

ОК, это нормально, я не вызываю какую-либо функцию, не являющуюся константой. Я тоже не хочу его менять. Я использую это потому, что я использую 'StoringClass', который нуждается в const char * & и я строю эту строку только один раз –

+0

@Yogesh Arora Вы должны изменить' const char * & data' на 'const char * data' –

+1

@Yogesh: Если все, что вы хотите сделать, действительно, для чего вам нужна ссылка, в первую очередь? – UncleBens

-1

Он может быть действительным. Однако вы не должны зависеть от этого поведения.

У реализации класса string есть char* внизу, а метод c_str() скорее всего возвращает указатель на начало этого массива.Однако, поскольку строка изменяется со временем, этот внутренний массив может перемещаться в памяти, изменять размер, вообще. Вы должны вызывать c_str() каждый раз, когда хотите конвертировать string в char*.

Скорее всего, он вернет указатель на внутреннюю память, так что это очень быстро.

+1

Я не вижу здесь значимости, он говорит о ссылке на указатель, не так ли? Не имеет смысла этот указатель. – falstro

+0

@roe 'Мой вопрос: правильно ли будут данные корректно ссылаться на содержимое теста? – pajton

1

Вы не должны изменять строковый буфер напрямую, если это то, что вы пытаетесь сделать.

Когда вы имеете дело с указателями в целом, вам нужна только ссылка на указатель, если вы хотите изменить адрес, который имеет тип указателя. Если вы хотите изменить буфер, вы можете просто сохранить char *.

Если вы хотите получить доступ к буферу, как этого использует станд :: вектор вместо этого что-то вроде этого:

std::vector<char> v; 
v.resize(size); 
strcpy(&v.front(), "testing"); 
1

Смотрите следующий комментарий о функции члена станд :: строка c_str() из cplusplus.com:

Возвращенный массив указывает на внутреннее местоположение с необходимым пространством хранения для этой последовательности символов плюс его завершающий нулевой символ, но значения в этом массиве не должны быть изменены в программе и предоставляются только в том случае, если они остаются неизменными до следующего функция непостоянного члена строкового объекта.

Хотя вы не называете какие-либо функции не-const-члена в коде, который вы опубликовали, это не очень хорошая идея. Когда вы изменяете приватную тестовую переменную std :: sting в классе RefPtrTest, вы непреднамеренно влияете на класс StoringClass (это нарушает инкапсуляцию RefPtrTest). Кто-то, поддерживающий ваш код позже (возможно, вы), может не понимать этого и вводить ошибку/сбой.

Смежные вопросы