@MikhailEdoshin

Что подразумевает C++ класс с private конструктором и парным auto_ptr?

Подскажите, пожалуйста — у меня имеется некое C/C++ API. Си-часть у него не совсем Си, потому что, хоть оно и объявлено extern "C", но внутри у него идет C++ код (например, используются ссылки, классы, пространства имен и т. п.; то есть единственное, что там «Си», это сохраненные имена функций). Си-плюс-плюс-часть у него представлена несколькими классами, но классы «пустые» — весь их код находится тут же в заголовочных файлах в виде inline-функций, которые, в свою очередь, вызывают методы из псевдо-Си части.

Я, соответственно, пустую часть игнорирую и работают с псевдо-Си частью. Кроме того, я работаю из обычного C — я просто передекларировал функции сам, заменяя ссылки на указатели, объявил неполные типы, где нужно и так далее. Все работает замечательно.

Но есть нюанс. Что я не совсем понимаю, так это то, что имел в виду автор исходного API. Например:

1. Функция возвращает константную ссылку, т. е.

const foo::Bar& myfunc(); 

В своем C-адаптере я переопределяю ее так:

const foo_Bar_t* myfunc();

Правильно ли я понимаю, что я не могу хранить этот указатель после возврата из функции?

2. Класс с private-конструктором и сопутствующий auto_ptr. Выглядит это так:

namespace foo {
    class Baz
    {
        private: 
            Baz();
            Baz( const Baz& source);
    };
    class BazAutoPtr: public std:auto_ptr<Baz>
    {
        public:
            inline BazAutoPtr();
    };
} 

BazAutoPtr() являет собою следующее:

inline BazAutoPtr::BazAutoPtr() {
    this->operator=(*static cast<Baz>(&std::auto_ptr<element_type>(Baz_Constructor())));
}

Здесь Baz_Constructor — это функция из псевдо-Си части, которая, собственно, создает новый Baz и возвращает указатель:

foo::Baz* Baz_Constructor();

Как я все это понимаю, объекты foo::Baz напрямую создавать нельзя, только через auto_ptr. Код в BazAutoPtr() вызывает реальный конструктор и подменяет там только что созданный пустой объект. Я также представляю смысл auto_ptr — выделить объект в куче и автоматом его удалить при выходе из функции. Что я не понимаю, так это что имел в виду автор API, сооружая вот эту конструкцию? Имел ли он в виду, что я вообще не должен хранить экземпляры foo::Baz или же что-то другое?

(Чисто технически я их храню пока без видимых последствий; но кто его знает.)
  • Вопрос задан
  • 5123 просмотра
Пригласить эксперта
Ответы на вопрос 3
Monnoroch
@Monnoroch
Мне кажется, с таким подходом вы все равно словите полсотни разных UB, так что в любом случае не советую так делать, а советую писать обертки. А автор кода очень странный. Мало того, что auto_ptr уже давно deprecated, так еще и наследоваться от него — это явно стремно. Резюмируя, тут автор что угодно мог подразумевать, от синтаксического сахара, до каких-то правил использования, и мне кажется, кроме как спросить его, нет способа понять.
Ответ написан
@Lol4t0
const foo::Bar& myfunc(); 

В своем C-адаптере я переопределяю ее так:

const foo_Bar_t* myfunc();

Вообще говоря, это уже неопределенное поведение, потому что стандарт не гарантирует, что ссылки будут реализованы посредством указателей.

Правильно ли я понимаю, что я не могу хранить этот указатель после возврата из функции?

Все зависит от того, что делает функция:

const int g = 1;
struct S
{
	const int & foo() const
	{
		return v;
	}
	const int & bar() const
	{
		return g;
	}
	int v = 0;
};

const int & foobar()
{
	S s;
	//return s.foo(); // oops
	return s.bar(); //ok
}

int main(int argc, char const *argv[])
{
	return foobar();
}


Дальше у вас идет некорректный код:
Как должен работать static_cast от std::auto_ptr<Baz>* в Baz?
Я предполагаю, что у вас там на самом деле написано:
this->operator=(*static_cast<BazAutoPtr*>(&std::auto_ptr<element_type>(Baz_Constructor())));

Что в общем тоже неопределенное поведение, потому что
error: taking address of temporary
static_cast объекта к типу, объектом которого он на самом деле не является.

Выражать эта конструкция должна была видимо примерно то же, что и
inline BazAutoPtr():std::auto_ptr<Baz>(Baz_Constructor()){}


В общем, самый дельный совет тут — завязывать с кодом, в котором такое написно.
Ответ написан
@Door
Думаю, в первом случае, лучше всего будет вернуть просто foo_Bar_t (зависит от того, что внутри foo_Bar_t).
А второе — бредятина полная: нельзя наследоваться от std:auto_ptr… В std:auto_ptr не переопределён оператор взятия адреса (operator&) и приведение типа std:auto_ptr<Baz>* в Baz — не должно скомпилироваться. И если уже писать такой бред, то, наверное, лучше сделать так:
inline BazAutoPtr::BazAutoPtr()
{
    reset(Baz_Constructor());
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы