2012-04-24 7 views
7

Это может быть, кажется, глупый вопрос, но я на самом деле нужно уточнить это:C++ Const литая, не зная, если это безопасно

Будет ли это принести никакой опасности для моей программы?

Нужно ли const_cast?

Если я изменю значения входных указателей на месте, он будет работать безопасно с std::string или это создаст неопределенное поведение?

Пока что единственная проблема заключается в том, что это может повлиять на строку «some_text» всякий раз, когда я изменяю указатель ввода и делает его непригодным.

std::string some_text = "Text with some input"; 

char * input = const_cast<char*>(some_text.c_str()); 

Спасибо за предоставленную мне несколько советов, я хотел бы избежать стрелять в моей ноге

+0

Как правило, поскольку 'const_cast' удаляет безопасность, вы должны избегать его как можно больше. – Mesop

+0

Спасибо за вклад всех тел. Теперь я понимаю, что я должен избегать этого, чтобы оставаться в безопасности. –

ответ

13

В качестве примера поведения зла: взаимодействие с реализацией gcc's Copy On Write.

#include <string> 
#include <iostream> 

int main() { 
    std::string const original = "Hello, World!"; 
    std::string copy = original; 

    char* c = const_cast<char*>(copy.c_str()); 
    c[0] = 'J'; 

    std::cout << original << "\n"; 
} 

В действии на ideone.

Jello, World!

Вопрос? Как следует из названия, реализация gcc std::string использует подсвеченный общий буфер под обложкой. Когда строка будет изменена, реализация будет аккуратно проверять, является ли общий доступ к буферу в данный момент, и если это так, скопируйте его перед его модификацией, гарантируя, что новая строка, использующая этот буфер, не будет затронута новой записью (таким образом, имя, копирование при записи).

Теперь, используя вашу злую программу, вы получаете доступ к общему буферу через const-метод (обещая ничего не изменять), но вы его изменяете!

Обратите внимание, что при реализации MSVC, который не использует Copy On Write, поведение будет иным ("Hello, World!" будет правильно напечатано).

Это как раз сущность Неопределенное поведение.

+16

Суть * Undefined Behavior *: Ваш мир превращается в _jello_. –

+0

Спасибо за это понимание –

5

Чтобы изменить неотъемлемо const объект, отбрасывая его константность использованием const_cast является Неопределенное поведение.

string::c_str() возвращает const char *, то есть: указатель на постоянную строку с стилем. Технически, изменение этого приведет к неопределенному поведению.

Обратите внимание, что использование const_cast - это то, что у вас есть указатель const на данные не const, и вы хотите изменить непостоянные данные.

+0

Я не нашел параметр строки, который выполняет операцию c_str() без const, есть ли альтернатива, которая является предпочтительной? –

+0

Может быть, 'c_str' возвращает указатель на массив' char', а не 'const char'. Формально вы можете сказать, что вы не можете гарантировать правильность программы, если она изменяет объекты через 'input', а не гарантирует, что программа неверна. –

+0

@OliverStutz: у вас должна быть другая достаточно распределенная переменная (массив или указатель), скопируйте ее в строку и затем измените строку, скопированную на другую переменную. –

1

Да, это принесет опасность, потому что

  1. input указывает на то, что c_str случается прямо сейчас, но если some_text когда-либо изменения или уходит, вы будете оставаться с указателем, который указывает на мусор , Значение c_str будет действительным только после того, как строка не изменится. И даже формально, только если вы не вызываете c_str() на другие строки.
  2. Зачем вам нужно отбрасывать const? Вы не планируете писать до *input, не так ли? Это не-нет!
+0

Это было именно то, что я хотел сделать, чтобы изменить строку (например, удалить повторяющиеся символы), моя самая большая проблема заключалась в том, что она фактически позволяет мне ее компилировать и запускать, но именно это заставило меня открыть этот пост, потому что это кажется нелогичным приложением const, и после этого им удалось правильно его написать и изменить (ну, вероятно, он уже идет на юг, но его не видно) –

+1

@OliverStutz Такие вещи, как удаление повторяющихся символов, можно выполнить, вызвав встроенную строку 'std :: string' функции. Но если вам нравятся старые функции C лучше, пройдите старый C полностью и сделайте 'strcpy' первым! –

2

std::string управляет ею собственной памяти внутри, поэтому она возвращает указатель на эту память непосредственно, как это делает с помощью функции c_str(). Он гарантирует, что он будет постоянным, чтобы ваш компилятор предупредил вас, если вы попытаетесь его модифицировать.

Использование const_cast таким образом буквально отбрасывает такую ​​безопасность и является только приемлемой практикой, если вы абсолютно уверены, что память не будет изменена.

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

2

См C++ reference сайт:

const char* c_str () const; 

«Создает заканчивающуюся нулем последовательность символов (с-строка) с тем же содержанием, как строковый объект и возвращает его как указатель на массив символов.

Конечный нулевой символ автоматически добавляется.

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

4

Просто литье не принесет неопределенное поведение. Изменение данных указал на, однако, будет. (Also see ISO 14882:98 5.2.7-7).

Если вы хотите указатель на изменяемые данные, вы можете получить

std::vector<char> wtf(str.begin(), str.end()); 
char* lol= &wtf[0]; 
+7

wtf & lol - новый foo & bar? – gwiazdorrr

+1

За исключением того, что теперь у вас есть строка с нулевым завершением. которые у вас нет с 'char * c = str.c_str(); std :: vector foo (c, c + str.size() + 1); ' – stefaanv

1

Это очень плохо. Посмотрите, что std :: string :: c_str() does и соглашайтесь со мной.

Во-вторых, подумайте о том, почему вы хотите неконстантный доступ к внутренностям строки std ::. Очевидно, вы хотите изменить содержимое, потому что иначе вы бы использовали указатель const char. Также вы обеспокоены тем, что вы не хотите изменять исходную строку. Почему бы не написать

std::string input(some_text); 

Тогда у вас есть зЬй :: строка, вы можете связываться с не затрагивая оригинал, и вы StD :: функциональных строк вместо того, чтобы работать с сырым C++ указатель ...

+0

Если OP нуждается в символе' char * ', то делать это нехорошо, потому что у вас есть те же проблемы с новым строка как с оригинальной! –

1

Другим поворотным моментом является то, что он делает код чрезвычайно сложным для обслуживания. Пример: несколько лет назад мне пришлось реорганизовать некоторый код, содержащий длинные функции. Автор написал сигнатуры функций для принятия константных параметров, но затем был const_cast их внутри функции для удаления константы. Это нарушило подразумеваемую гарантию, данную функцией, и очень затруднительно узнать, изменился или не изменился параметр внутри остальной части кода.

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

1

это UB. Например, вы можете сделать что-то вроде этого: это

size_t const size = (sizeof(int) == 4 ? 1024 : 2048); 
int arr[size]; 

без броска и comiler не сообщит об ошибке. Но этот код является незаконным. Мораль состоит в том, что вам нужно учитывать действие каждый раз.