путаница преобразования строк, строк и символов *

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

  1. Используйте оператор instanceof.
  2. Используйте this.getClass().equals(that.getClass()).

Я использую # 1 в реализации final equals или при реализации интерфейса, который предписывает алгоритм для equals (например, java.util интерфейсы коллекции - правильный путь для проверки с помощью (obj instanceof Set) или любого другого интерфейса, который вы реализуете). Обычно это плохой выбор, когда equals можно переопределить, потому что это нарушает свойство симметрии.

Вариант № 2 позволяет безопасно расширять класс без превышения равных или нарушения симметрии.

Если ваш class также Comparable, методы equals и compareTo также должны быть последовательными. Вот шаблон для метода equals в классе Comparable:

final class MyClass implements Comparable<MyClass>
{

  …

  @Override
  public boolean equals(Object obj)
  {
    /* If compareTo and equals aren't final, we should check with getClass instead. */
    if (!(obj instanceof MyClass)) 
      return false;
    return compareTo((MyClass) obj) == 0;
  }

}
135
задан jww 6 May 2015 в 06:27
поделиться

5 ответов

stringstream.str () возвращает временный строковый объект, который уничтожается в конце полного выражения. Если вы получите указатель на строку C из этого ( stringstream.str (). C_str () ), он укажет на строку, которая будет удалена там, где заканчивается оператор. Вот почему ваш код печатает мусор.

Вы можете скопировать этот временный строковый объект в какой-либо другой строковый объект и взять из него строку C:

const std::string tmp = stringstream.str();
const char* cstr = tmp.c_str();

Обратите внимание, что я сделал временную строку const , потому что любые изменения в ней могут вызвать ее перераспределить и, таким образом, сделать cstr недействительным. Поэтому безопаснее вообще не сохранять результат вызова str () и использовать cstr только до конца полного выражения:

use_c_str( stringstream.str().c_str() );

Конечно, последнее может оказаться непростым, а копирование - слишком дорогим. Вместо этого вы можете привязать временное значение к ссылке const . Это продлит срок его службы до срока службы ссылки:

{
  const std::string& tmp = stringstream.str();   
  const char* cstr = tmp.c_str();
}

ИМО, это лучшее решение. К сожалению, это не очень хорошо известно.

195
ответ дан 23 November 2019 в 23:34
поделиться

Что вы делание - это создание временного. Этот временный объект существует в области видимости, определяемой компилятором, так что он достаточно длинный, чтобы удовлетворить требованиям, в котором он находится.

Как только оператор const char * cstr2 = ss.str (). C_str () ; завершено, компилятор не видит причин для сохранения временной строки, и она уничтожается, и, таким образом, ваш const char * указывает на освобожденную память.

Ваш оператор ] string str (ss.str ()); означает, что временная переменная используется в конструкторе для string переменной str , которую вы поместили в локальный стек, и остается там столько, сколько вы ожидаете: до конца блока или написанной вами функции. Поэтому const char * в пределах все еще остается хорошей памятью, когда вы пробуете cout .

13
ответ дан 23 November 2019 в 23:34
поделиться

В этой строке:

const char* cstr2 = ss.str().c_str();

ss.str () сделает копию содержимого строкового потока. Когда вы вызываете c_str () в той же строке, вы будете ссылаться на допустимые данные, но после этой строки строка будет уничтожена, а ваш char * будет указывать на незарегистрированную память. .

5
ответ дан 23 November 2019 в 23:34
поделиться

Объект std :: string, возвращаемый ss.str (), является временным объектом, время жизни которого будет ограничено выражением. Таким образом, вы не можете назначить указатель на временный объект без получения мусора.

Теперь есть одно исключение: если вы используете константную ссылку для получения временного объекта, законно использовать ее для более длительного срока службы. Например, вы должны сделать:

#include <string>
#include <sstream>
#include <iostream>

using namespace std;

int main()
{
    stringstream ss("this is a string\n");

    string str(ss.str());

    const char* cstr1 = str.c_str();

    const std::string& resultstr = ss.str();
    const char* cstr2 = resultstr.c_str();

    cout << cstr1       // Prints correctly
        << cstr2;       // No more error : cstr2 points to resultstr memory that is still alive as we used the const reference to keep it for a time.

    system("PAUSE");

    return 0;
}

Таким образом, вы получите строку на более длительное время.

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

5
ответ дан 23 November 2019 в 23:34
поделиться

Временный объект ss.str () уничтожается после завершения инициализации cstr2 . Итак, когда вы печатаете его с помощью cout , c-строка, связанная с этим временным std :: string , уже давно уничтожена, и поэтому вам повезет, если она выйдет из строя и подтвердит , и не повезет, если он печатает мусор или работает.

const char* cstr2 = ss.str().c_str();

C-строка, на которую указывает cstr1 , однако, связана со строкой, которая все еще существует в то время, когда вы выполняете cout , поэтому она правильно печатает результат.

В следующем коде первый cstr правильный (я предполагаю, что это cstr1 в реальном коде?). Второй выводит c-строку, связанную с временным строковым объектом ss.str () . Объект уничтожается в конце оценки полного выражения, в котором он появляется. Полное выражение - это весь cout << ... выражение - поэтому, пока c-строка выводится, связанный строковый объект все еще существует. Для cstr2 - это чистая дурь, что ему удается. Скорее всего, он внутренне выбирает то же место хранения для нового временного файла, который он уже выбрал для временного, используемого для инициализации cstr2 . Он мог также разбиться.

cout << cstr            // Prints correctly
    << ss.str().c_str() // Prints correctly
    << cstr2;           // Prints correctly (???)

Возврат c_str () обычно просто указывает на внутренний строковый буфер, но это не является обязательным требованием. Строка может составлять буфер, например, если ее внутренняя реализация не является непрерывной (это вполне возможно, но в следующем стандарте C ++ строки должны храниться непрерывно).

В GCC строки используют подсчет ссылок и копирование при записи. Таким образом, вы обнаружите, что верно следующее (так оно и есть, по крайней мере, в моей версии GCC)

string a = "hello";
string b(a);
assert(a.c_str() == b.c_str());

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

вы обнаружите, что верно следующее (так оно и есть, по крайней мере, в моей версии GCC)

string a = "hello";
string b(a);
assert(a.c_str() == b.c_str());

Две строки здесь используют один и тот же буфер. При изменении одного из них буфер будет скопирован, и каждый будет содержать свою отдельную копию. Однако другие строковые реализации работают иначе.

вы обнаружите, что верно следующее (так оно и есть, по крайней мере, в моей версии GCC)

string a = "hello";
string b(a);
assert(a.c_str() == b.c_str());

Две строки здесь используют один и тот же буфер. При изменении одного из них буфер будет скопирован, и каждый будет содержать свою отдельную копию. Однако другие строковые реализации работают иначе.

5
ответ дан 23 November 2019 в 23:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: