Повреждение StringStream/c_str() в C++

У меня возникли проблемы с std::cout, std::stringstream и std::string.c_str(). В основном, кажется, что где-то что-то застревает в буфере, и я не знаю, как решить эту проблему.

Если вам не нравится читать код в StackOverflow, вот соответствующие ссылки на мой github: класс TLString, тестовый класс и модульный тест — и вы можете перейти к концу, где я формулирую свой более краткий вопрос.

В моем модульном тесте у меня есть следующий код:

    Test <std::string> strtest; // A unit test object expecting strings.
    Test <const char*> chtest; // A unit test object expecting const char*s
    // ...

    TurnLeft::Utils::TLString str3("Exterminate.");
    // ...

    /* Basically, name() and expect() will use the passed arg. 
     * in the output in order to
     * display output such as the following:
     * str(): expecting 'Exterminate.' | Actual 'Exterminate.' => PASS
     */
    strtest.name("str()").expect("Exterminate.").test( str3.str() );

    /* To try and determine where the corruption was occuring, I did a 
     * simple cout here, and got what should be the expected result of 
     * the next test,
     * meaning that the actual test should be succeeding.
     */
    std::cout << str3.c_str() << std::endl //outputs Exterminate. normally.

    /* But when I try to invoke that same method (c_str()) within the test
     * object, it simply copies the argument passed in for name().
     */
    chtest.name("c_str()").expect("Exterminate.").test( str3.c_str() );
    // Should output 'Exterminate.' as in the saatement before, but instead
    // outputs 'c_str()'.

Вот код класса Test:

namespace unittest{
static std::string status[2] = {"FAIL", "PASS"};

    template <class ExpectedResult>
    class Test
    {
     private:
        ExpectedResult  expected;
        ExpectedResult  actual;
        std::string     testName;
     public:
        Test();
        Test <ExpectedResult>& expect (ExpectedResult value);
        Test <ExpectedResult>& name   (std::string);
        void test   (ExpectedResult value);
    };

template <class ExpectedResult> Test <ExpectedResult>&
Test<ExpectedResult>::expect(ExpectedResult value)
{    
    expected = value;
    return *this;
}

template <class ExpectedResult>
Test <ExpectedResult>&
Test<ExpectedResult>::name(std::string aName)
{
    testName = aName;
    return *this;
}

    template <class ExpectedResult>
    void Test<ExpectedResult>::test(ExpectedResult value)
    {
        actual = value;
        std::cout << testName << ": ";
        std::cout << "Expecting: " << expected << " | ";
        std::cout << "Actual: " << actual;
        std::cout << " => " << status[actual==expected] << std::endl;
    }

Я пишу класс TLString, который обеспечит более плавную работу со строками в C++ (например, конкатенацию). Он использует строковый поток для обработки этих операций. Метод TLStream::c_str() на самом деле просто делает это: return stream.str().c_str();

Итак, я действительно смущен тем, как actual присваивается значение testName. Я не уверен, где возникает конфликт, учитывая, что единственный раз, когда переменные to приближаются к взаимодействию, - это когда они оба выводятся в CLI, и даже более того, поскольку в этом случае эти два являются разными типами данных.

Я написал функциональность c_str(), потому что, проще говоря, никогда не знаешь, когда какая-то сторонняя библиотека будет полагаться на строки C вместо строк C++, и не видел причин ограничивать мой класс. Даже в std::ios вам нужно использовать строки c для некоторых вещей.

Любая помощь будет принята с благодарностью.

Спасибо!


person Thomas Thorogood    schedule 17.05.2012    source источник
comment
Просто интересно, что плохого в использовании append или operator+/operator+= для конкатенации?   -  person chris    schedule 17.05.2012
comment
Я реализую оператор+ и оператор+= в классе, который я пишу; Я не счел разумным перегружать оператор стандартным библиотечным объектом. Есть также некоторые другие функции, которые я добавляю. Однако я не знал о добавлении. Так что это супер полезно.   -  person Thomas Thorogood    schedule 17.05.2012
comment
Ба, я думал, что в этой ветке есть все исправления. Я также заметил, пытаясь решить эту проблему, что возвращал копию объекта вместо ссылки на тот же объект. Фактическое определение читается как Test‹ExpectedResult›& name(std::string); Я, должно быть, не добавил и не зафиксировал после внесения этих изменений. Мои извенения.   -  person Thomas Thorogood    schedule 17.05.2012


Ответы (2)


std::stringstream.str() возвращает временный объект типа std::string. Это временное значение выходит из области видимости, когда возвращается TLStream::c_str(), оставляя возвращаемый указатель char const*, указывающий на освобожденную память.

person Dark Falcon    schedule 17.05.2012
comment
оооооооооооооооооооооооооооооооооооооооооооооооооооо. Очаровательный. Думаю, тогда я просто откажусь от этого метода. В редких случаях мне нужно будет получить строку c, я просто вызову ее для объекта возвращаемой строки из метода str() вместо того, чтобы пытаться использовать двойную функцию. - person Thomas Thorogood; 17.05.2012

    std::cout << " => " << status[actual==expected] << std::endl;

Этот == сравнивает const char*, указывающий на строковый литерал "Exterminate", с const char*, указывающим на str3.c_str()

Эти указатели разные.

Вам нужно сравнить их, используя что-то вроде strcmp, а не равенство указателей.

person Jonathan Wakely    schedule 17.05.2012
comment
Также верно, но в соответствии с приведенным выше ответом я полностью удаляю этот метод, поскольку более элегантно просто возвращать строку С++ и вызывать для нее метод c_str() при необходимости. - person Thomas Thorogood; 17.05.2012