Константные строковые литералы С++ и пользовательский строковый класс

В C++ строковые литералы "Hello" являются постоянными и неизменяемыми. Я хотел создать собственный класс строк, строки которого не являются константными символами, чтобы их можно было изменить.

Вот фрагмент кода, который может иллюстрировать то, что я пытаюсь сделать:

#include <iostream>

class String {
  public: 
    char * p_start;

    String(char * strSourc) // Constructor
      {
        p_start = strSourc;
      }
};

int main() 
{
  String myString("Hello");  
// Create object myString, send "Hello" string literal as argument

  std::cout << myString.p_start << std::endl; 
// Prints "Hello"

  *myString.p_start = 'Y'; 
// Attempt to change value at first byte of myString.p_start

  std::cout << myString.p_start << std::endl; 
// Prints "Hello" (no change)

  myString.p_start = "Yellow"; 
// Assigning a string literal to p_start pointer 

  std::cout << myString.p_start << std::endl; 
// Prints Yellow, change works.  I thought myString "Hello" was const chars, immutable

  return 0;
}

Итак, я в замешательстве. Я искал везде, и там говорится, что строковые литералы, такие как «Hello», неизменяемы, каждый из их байтов char неизменен. Хотя мне удалось присвоить Yellow указателю p_start, изменив первую букву. Хотя изменение одной буквы H на Y путем разыменования указателя H ничего не дало.

Любые идеи помогут мне, спасибо.


person Zebrafish    schedule 03.10.2015    source источник
comment
Не могли бы вы показать свой текущий ctor? И с какой ошибкой вы столкнулись?   -  person songyuanyao    schedule 03.10.2015
comment
Я изменил вопрос, чтобы лучше проиллюстрировать проблему, включая пример кода.   -  person Zebrafish    schedule 03.10.2015
comment
Да, вы не можете изменить содержимое Hello своим указателем p_start. Вы можете сделать его копию в ctor, затем вы можете изменить символы, которыми управляет класс.   -  person songyuanyao    schedule 03.10.2015
comment
Спасибо за это. Чего я не понимаю, так это того, почему он позволил мне сказать p_start = Yellow, если строковый литерал представляет собой массив const char с нулевым завершением, но не позволил мне сказать *p_start = 'Y'. Кроме того, как вы сказали, мне придется передать ему строковый литерал, а затем сделать копию один к одному в мой собственный массив, но это поднимает вопрос, я слышал, что строковые литералы статичны и сохраняются навсегда во время программы до тех пор, пока программа останавливается. Не будет ли это огромной тратой памяти? В основном иметь две копии всего? Насколько я знаю, вы не можете уничтожить строковые литералы, это то, что я читал.   -  person Zebrafish    schedule 03.10.2015


Ответы (1)


Я думаю, вы путаете указатель и указатель.

p_start = "Yellow", вы меняете значение указателя, чтобы он указывал на "Желтый". *p_start = 'Y', вы меняете значение pointee, на которое указывает содержимое p_start, а не на себя. Как вы сказали, «Желтый» - это константные символы, поэтому поведение, пытающееся изменить их, - это UB.

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

person songyuanyao    schedule 03.10.2015
comment
Я проголосовал за ваш ответ, спасибо. Вы правы, я перепутал указатель и указатель. Если вы сделаете указатель на int, присвоите его адресу int, а затем cout ‹‹ указатель, вы получите адрес памяти, на который указывает указатель. Если, с другой стороны, как я сделал здесь, вы создаете массив символов, а затем делаете указатель, указывающий на массив символов, и cout ‹‹ указатель, вы не получите адрес, на который указывает указатель, вы получите всю строку с нулевым завершением. Это почему? Разные правила? Кроме того, как я могу назначить желтый 4-байтовый указатель? - person Zebrafish; 04.10.2015
comment
Я только что узнал, что когда я сделал p_start = Yellow, он не присвоил значение Yellow p_start, а создал новый блок памяти со строковым литералом Yellow и передал его адрес p_start. В любом случае, я должен сказать, что я считаю, что строковые литералы неизменны и статичны, это заноза в заднице. Я уверен, что у них есть свои причины, но если я хочу манипулировать строкой, я должен либо скопировать ее из строкового литерала и иметь две копии (потому что оригинал статичен и никогда не уничтожается), либо создать массив и отправить его в мой класс в два этапа. Кто-нибудь думает, что это глупо? - person Zebrafish; 04.10.2015
comment
@TitoneMaurice Другие правила? Да, operator<<() перегружает char * для вывода содержимого строки, поэтому поведение отличается от int * или float * и так далее. Как я могу назначить желтый 4-байтовый указатель? Вы не назначаете строку указателю, а только первый адрес строки, то есть адрес Y в желтом цвете. - person songyuanyao; 04.10.2015
comment
@TitoneMaurice Поскольку строковый литерал нельзя изменить, компилятору разрешено выполнять над ним некоторую оптимизацию, например сохранять его в ПЗУ и только одну копию в памяти для всех строковых литералов с одинаковым значением и так далее. - person songyuanyao; 04.10.2015
comment
Да я понимаю, что ты говоришь. Таким образом, строковые литералы являются константными символами, за исключением случаев, когда вы делаете следующее: char myCharArray[ ] = Hello Я рад, что обычные строковые литералы C++ должны быть константными. Но моя точка зрения заключается в том, что я хочу, чтобы мой строковый класс мог принимать объявления, например строку myString(hello), и мог манипулировать символами (неконстантными). Я знаю, что могу сначала создать массив символов, а затем передать указатель, это два шага. Точно так же создание константной строки, а затем ее копирование, и это будет длиться НАВСЕГДА как двойная копия, меня не устраивает, если я имею дело с большим количеством строк. - person Zebrafish; 04.10.2015
comment
Я приведу вам пример того, почему я жалуюсь. Эти комментарии позволяют вам 600 символов. Каждый комментарий будет состоять из 601 байта в виде строки. Я передаю эти строки моему классу строк и делаю одну копию каждого символа в выделенную память. Если бы я хотел иметь сотню строк, это заняло бы 12 200 байт (6100 * 2), 6100 из которых были бы бесполезны и хранились бы вечно в стеке, и мне пришлось бы делать 6100 копий один к одному. В противном случае я мог бы создать 100 массивов символов, а затем передать указатель на мой строковый класс и скопировать указатели на мой строковый класс 100 раз. И это только со 100. Понял? - person Zebrafish; 04.10.2015
comment
@TitoneMaurice Если у вас много строковых литералов, они будут стоить много воспоминаний, это правда. Но я не думаю, что это частый случай. Мы больше создаем строки из пользовательского ввода, из сети и так далее. - person songyuanyao; 04.10.2015
comment
Комментарии @TitoneMaurice не могут быть строковыми литералами. Они не могут быть решены во время компиляции. const char[] comment1 = "???"; - person songyuanyao; 04.10.2015
comment
Давайте продолжим обсуждение в чате. - person Zebrafish; 04.10.2015