std :: string теряет значение при передаче в функцию внутри объекта класса

Я действительно запутался, как компилятор распределяет объекты STL. Рассмотрим следующий код:

#include <string>

using namespace std ;

class s {
public:
string k ;

s(string k) : k(k) {}
} ;

void x ( s obj ) {
string k = (obj.k) ;
k += "haha" ;

}int main () {
std::string mystr ("laughter is..") ;
s mys(mystr) ;
x(mys) ;

printf ("%s", mystr.c_str() ) ;
}

Выход этой программы laughter is.. и я ожидаю, что результат будет:
laughter is haha

Почему строка mystr не получается haha , Мне нужно хранить его в классе как часть моего кода.

Если бы я передал mystr по значению в функцию x, строка mystr получила бы haha внутрь.

а) Как и когда выделяются объекты STL? Я предполагал, что mystr находится в стеке и должен быть доступен для всех функций, вызываемых из main ().

б) Что делать, если мне нужно хранить объекты STL в старомодном связанном списке, для которого требуется «void *». Не могу я просто сделать:

std::string mystr ("mystring.." );

MyList.Add((void*)&mystr) ;

fun(MyList) ;

Может ли функция доставлять удовольствие, теперь использовать и изменять mystr, получая доступ к MyList?

в) В качестве альтернативы (б) я могу использовать передачу по ссылке. Вопрос в том, могу ли я объявить класс, чтобы сохранить ссылку на mystr? Я имею в виду, что конструктор MyList может быть таким:

class MyList {
string& mStr ;
...
};MyList::MyList ( string& mystr ) {
mStr = mystr ;

}

Этот конструктор действителен? Этот класс действителен?

0

Решение

Ваш класс просто осложняет ситуацию для вас. У вас точно такая же проблема здесь:

void x ( string str ) {
str += "haha" ;
}

int main () {
std::string mystr ("laughter is..") ;
x(mystr) ;

printf ("%s", mystr.c_str() ) ;
}

Я избавился от класса. Вместо того, чтобы положить mystr в s объект и прохождение s Возражать xЯ просто прохожу mystr непосредственно. x затем пытается добавить "haha" в строку.

Проблема в том, что x принимает свой аргумент по значению. Если вы передадите объект по значению, вы получите его копию. Это str объект является другим объектом для mystr, Это его копия, но это другой объект. Если вы измените str, ты не собираешься влиять mystr совсем.

Если бы вы хотели x чтобы иметь возможность изменить его аргумент, вам нужно заставить его взять ссылку:

void x ( string& str ) {
str += "haha" ;
}

Однако я понимаю, почему вы представили класс. Вы думаете: «Если я передам строку другому объекту, а затем передам этот объект, строка должна быть одинаковой как внутри, так и внутри функции». Это не так, потому что ваш класс хранит копию строки. То есть в вашем классе есть член string k; которые будут часть любой объект этого типа класса. Строка k не тот же объект, что и mystr,

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

Что касается ваших вопросов:

  1. Да, string объект mystr находится в стеке. Это не имеет ничего общего с исходной библиотекой. Если вы напишите объявление внутри функции, этот объект будет в стеке, будь то int x;, string s;, SomeClass c;или что угодно.

    Внутреннее хранилище данных внутри mystr с другой стороны, динамически распределяется. Это должно быть потому, что размер std::string может варьироваться, но объекты в C ++ всегда имеют фиксированный размер. Некоторое динамическое распределение необходимо. Однако вам не нужно заботиться об этом. Это распределение инкапсулировано классом. Вы можете просто лечить mystr как строка

  2. Пожалуйста, не используйте связанный список, который хранит void*s. использование std::list вместо. Если вы хотите связанный список строк, вы хотите std::list<std::string>, Но да, если у вас есть объект, который хранит указатели на некоторые другие объекты, и вы передаете этот объект по значению, указатели в копиях будут по-прежнему указывать на те же места, поэтому вы все равно сможете изменять объекты, на которые они указывают.

  3. Если у тебя есть std::list<std::string> и вы хотите передать его функции, чтобы функция могла изменять содержимое контейнера, тогда вам нужно передать его по ссылке. Если вам также нужно, чтобы элементы списка были ссылками на объекты, которые вы создали вне списка, вам нужно использовать std::list<std::reference_wrapper> вместо.

    Что касается инициализации ссылочного элемента, вам необходимо использовать список инициализации элемента:

    MyList::MyList(string& mystr)
    : mStr(mystr)
    { }
    
3

Другие решения

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

На другие ваши вопросы:
а) Да, объекты таким образом распределяются в стеке. Не просто STL, любые объекты. В противном случае вам нужно использовать new,
б) Нет, вы не можете передать его таким образом, так как он выделен стеком, и память станет недействительной. Вам нужно выделить кучу, используя new.
в) Да, вы можете перейти по ссылке, но опять же, важно, где вы размещаете вещи.

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

0

Строго говоря, ваш вопрос не имеет ничего общего с STL, даже если вы принимаете «STL» как синоним для правильных «контейнеров, итераторов и алгоритмов стандартной библиотеки C ++». std::string В какой-то момент история создавалась как контейнер (то есть контейнер символов), но обычно она используется совсем не так, как «реальные» классы контейнеров, такие как std::vector или же std::set,

Тем не мение,

Почему строка mystr не получает «ха-ха»

Потому что вы не используете Рекомендации. x модифицирует копия аргументации; то же самое, string k = (obj.k) создает копия строки. Вот код со ссылками:

void x ( s &obj ) {
string &k = (obj.k) ;
k += "haha" ;
}

а) Как и когда выделяются объекты STL?

Сам контейнерный объект выделяется так, как вы его определили. Как это распределяет память внутренне по умолчанию определяется его параметром шаблона распределителя std::allocator, Вы действительно не хотите знать внутренности std::allocator — это почти всегда делает правильные вещи. И я не думаю, что ваш вопрос о внутренних распределениях, так или иначе.

Я предполагал, что mystr находится в стеке и должен быть доступен для всех функций, вызываемых из main ()

Да.

б) Что делать, если мне нужно хранить объекты STL в старомодном связанном списке
который нуждается в «void *».

использование std::list<void*>,

Но ты не обязан это делать. использование std::list<std::string> и вам, вероятно, вообще не понадобятся указатели в вашем коде.

Что касается ваших дальнейших примеров кода:

std::string mystr ("mystring.." );
MyList.Add((void*)&mystr) ;
fun(MyList) ;

Может ли функция доставлять удовольствие, теперь использовать и изменять mystr, получая доступ к MyList?

Да. Однако у кода есть две проблемы. Меньший (void*)&mystr, Как правило, вы должны избегать бросков в стиле C, но используйте один из static_cast, reinterpret_cast, const_cast или же dynamic_castв зависимости от того, какое преобразование вам нужно. И в этом куске кода вам вообще не нужен приведение.

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

Чтобы решить эту проблему, вы должны узнать больше о new, delete и, возможно, умные указатели. Это выходит за рамки простого ответа, и результат, вероятно, все еще будет хуже, чем std::list<std::string>,

Вопрос в том, могу ли я объявить класс, чтобы сохранить ссылку на mystr?

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

class MyList {
string& mStr ;
...
};MyList::MyList ( string& mystr ) {
mStr = mystr ;

}

Этот конструктор действителен?

Нет, это не скомпилируется. Вам нужно будет использовать список инициализации:

MyList::MyList ( string& mystr ) : myStr(mystr) {}

Я могу только повторить мою рекомендацию сверху. использование std::list<std::string>.

0
По вопросам рекламы ammmcru@yandex.ru