C ++ - наследование сбоев Ostream на Android, но не Windows - PullRequest
2 голосов
/ 28 февраля 2012

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

Примечание: у меня есть 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;
    };

Это barebones, и он отлично работает на Windows. Он прекрасно компилируется на 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, но это должно быть что-то не так с конструктором, я полагаю, это не правильно?

Ответы [ 2 ]

5 голосов
/ 28 февраля 2012

В конструкторе сначала инициализируется базовый класс, а затем все члены. Когда вы вызываете конструктор базового класса 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 в исходном коде была излишней: когда вы определяете функцию-член в определении класса, она автоматически помечается как встраиваемая.

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

1 голос
/ 28 февраля 2012

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

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

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

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

...