Повреда на 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()'.

Ето кода за тестовия клас:

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. Не съм сигурен къде възниква конфликтът, като се има предвид, че единственият път, когато тези променливи се доближават до взаимодействие, е когато и двете са изведени към 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
Внедрявам operator+ и operator+= в класа, който пиша; Не смятах, че е разумно да претоварвам оператор със стандартен библиотечен обект. Има и други функции, които добавям. Не знаех обаче за append. Така че това е супер полезно.   -  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++ низ и да извикате метода c_str() върху него, когато е необходимо. - person Thomas Thorogood; 17.05.2012