2012-02-28 2 views
2

Я реализовал простой класс ostream и streambuf. По какой-то причине он падает, когда я пытаюсь создать экземпляр объекта AndroidLogOStream.C++ - наследование ostream сбоев на Android, но не windows

Примечания: У меня есть stlport_static в моем Application.mk

class AndroidLogStreamBuf : public std::streambuf 
    { 
    public: 
     inline AndroidLogStreamBuf() : std::streambuf() 
     { 
      //std::cout << "asdfg"; 

     } 

     inline ~AndroidLogStreamBuf() 
     { 

     } 



    }; 

    class AndroidLogOStream : public std::ostream 
    { 
    public: 
     inline AndroidLogOStream() : std::ostream(&mBuf) 
     { 

     } 

     inline ~AndroidLogOStream() 
     { 

     } 

    private: 
     AndroidLogStreamBuf mBuf; 
    }; 

Это скелетное, и он прекрасно работает на окнах. Он отлично компилируется на Android, но по какой-то причине он падает. В последней строке он пытается выполнить в _streambuf.c: 46:

template <class _CharT, class _Traits> 
locale 
basic_streambuf<_CharT, _Traits>::pubimbue(const locale& __loc) { 
    this->imbue(__loc);   <---- crash 
    locale __tmp = _M_locale; 
    _M_locale = __loc; 
    return __tmp; 
} 

Предоставленный Я еще совсем запутался iostreams, но это должно быть что-то неправильно с конструктором, я предполагаю, что это не действует?

+0

См. [Этот старый ответ] (http://stackoverflow.com/a/528661/440558) для некоторых полезных ссылок. –

+0

почему теги android и C++? я могу получить C++ один, но не другой! –

+0

Я запускаю его на Android с андроидом ndk: p – KaiserJohaan

ответ

5

В конструкторе сначала инициализируется базовый класс, за которым следуют все члены. Когда вы вызываете конструктор базового класса std::ostream, вы передаете ему адрес mBuf, который еще не построен. Доступ к объекту, который еще не был создан, имеет неопределенное поведение.

Чтобы обойти эту проблему, вы можете перепроектировать классы следующим образом:

class AndroidLogStreamBuf : public std::streambuf 
{ 
public: 
    AndroidLogStreamBuf() : std::streambuf() 
    { } 

    ~AndroidLogStreamBuf() 
    { } 
}; 

class AndroidLogOStream : public std::ostream 
{ 
public: 
    AndroidLogOStream(AndroidLogStreamBuf *buf) : 
     std::ostream(buf), 
     mBuf(buf) 
    { } 

    ~AndroidLogOStream() 
    { } 

private: 
    AndroidLogStreamBuf *mBuf; 
}; 

class AndroidLogOStreamWithBuf 
{ 
private: 
    AndroidLogStreamBuf mBuf; 
    AndroidLogOStream mStream; 

public: 
    AndroidLogOStreamWithBuf() : 
     mBuf(&mStream), 
     mStream() 
    { } 

    virtual ~AndroidLogOStreamWithBuf() 
    { } 

    AndroidLogOStream& getOStream() 
    { 
     return mStream; 
    } 
}; 

Обратите внимание на порядок я объявил mBuf и mStream в AndroidLogOStreamWithBuf: два поля будут инициализированы в таком порядке, независимо от того, порядок они появляются в списке инициализаторов конструктора. В стороне, отметив функции-члены как inline в вашем исходном коде, было излишним: когда вы определяете функцию-член в определении класса, она автоматически помечена как встроенная.

Является ли это разумным дизайном для вашей системы, зависит от того, как вы собираетесь использовать эти классы, но ответ, вероятно, «нет».

+0

Ahhhh! это хороший момент! Одна вещь: почему она отлично работает на окнах? – KaiserJohaan

+0

К сожалению, один из возможных результатов неопределенного поведения «работает отлично». – uesp

+0

как бы я решил это решить? Я имею в виду, что мне нужно передать буфер как референт в конструкторе ostream! – KaiserJohaan

1

Как уже указывалось, базовый класс строится первым, и из его внешнего вида конструктор базового класса, похоже, что-то делает. Я не думаю, что это предназначено, но деструктор базового класса также создаст проблему, и он вызовет в буфере потока pubsync().

Конечно, это объясняет проблему, но не дает решения: решение этой проблемы инициализации состоит в том, чтобы сделать буфер потока (или собственный класс, содержащий буфер потока как член) базовым классом virtual:

class oandroidligstream: 
    virtual AndroidLogStream, 
    public std::ostringstream { 
     ... 
    } 
}; 

Причина, по которой база должна быть виртуальной, заключается в том, что буфер потока является аргументом виртуальной базы std::ios. Чтобы убедиться, что ваш буфер потока инициализирован сначала, он должен быть самой левой виртуальной базой.

+0

, если я объявляю его виртуальным, он говорит: «mBuf undefined» в моем конструкторе AndroidLogOStream(), и я должен использовать mBuf в конструкторе. Как это решить? – KaiserJohaan

+0

Ну, это не член больше, а базовый класс! Вы можете использовать 'this'. В качестве альтернативы вы помещаете его в настраиваемую структуру и используете эту витражную основу. –