Вопрос

Часто я добавляю Empty к моим объектам C++, чтобы очистить внутреннее состояние, используя код, аналогичный следующему.

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo() : n_(1234), str_("Hello, world!")
    {
    }

    void Empty()
    {
        *this = Foo();
    }
};

Кажется, это лучше, чем дублировать код в конструкторе, но мне интересно, можно ли *this = Foo() является распространенным подходом при желании очистить объект?Есть ли какие-нибудь проблемы с этим ожиданием укусить меня за зад?Есть ли другие лучшие способы добиться такого рода вещей?

Это было полезно?

Решение

Я бы позволил конструктору вместо этого вызывать мою функцию:

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo()
    {
        Reset();
    }

    void Reset()
    {
        n_ = 1234;
        str_ = "Hello, world!";
    }
};

Да, вы сначала без необходимости инициализируете строку как пустую строку, затем выполняете присваивание, но это намного понятнее.

Другие советы

Потенциальные проблемы? Откуда ты знаешь, что * это действительно Foo?

То, что вы делаете с помощью этого метода Empty, по сути аналогично тому, как вручную назначать вновь созданный объект переменной (то, что делает функция Empty).

Лично я бы удалил метод Empty и заменил бы все виды использования этого метода следующим:

// let's say, that you have variables foo and pfoo - they are properly initialized.
Foo foo, *pfoo;

// replace line "foo.Empty()" with:
foo = Foo();

// replace line "pfoo->Empty()" with:
delete pfoo;
pfoo = new Foo();
// or
*pfoo = Foo();

Я действительно не вижу никакого преимущества в использовании этого пустого метода. Он скрывает то, что на самом деле происходит с объектом, на котором он назван, имя тоже не лучший выбор.

Если вызывающий объект действительно хочет получить чистый объект, у него не возникнет проблем с созданием самого объекта.

Кроме того, рассмотрите возможность сделать объекты неизменяемыми, т.е. , когда они созданы, их нельзя изменить. В большинстве случаев это может спасти вас от непредвиденных побочных эффектов.

Есть что-то даже более распространенное, чем то, что вы предложили. используя своп.

В основном вы делаете что-то вроде этого:

T().swap(*this);

, поскольку многие стандартные контейнеры (все контейнеры STL?) имеют метод подстановки с постоянным временем, это хороший и простой способ очистить контейнер и убедиться, что его хранилище освобождено.

Точно так же это хороший способ "сжать, чтобы соответствовать" контейнер, но с использованием конструктора копирования вместо конструктора по умолчанию.

рассмотрите возможность размещения new :

void Empty() {
    this->~Foo();
    new (this) Foo();
}

Ваш код вызывает operator = , что может привести к всевозможным побочным эффектам.

РЕДАКТИРОВАТЬ в ответ на комментарии. - Этот код определенно четко определен, стандарт явно разрешает это. Я собираюсь опубликовать абзац позже, если найду время. Насчет delete - конечно. Я имел в виду ~ Foo () , это был недосмотр. И да, Роб тоже прав; разрушение объекта здесь действительно необходимо для вызова деструктора строки.

Это может быть потенциальным источником утечки памяти, если у вас есть динамически распределенная память в конструкторе.

Вот как я это делаю:

class Foo {
private:
    int n_;
    std::string str_;
public:
    Foo() : n_(1234), str_("Hello, world!")
    {
    }

    void Empty()
    {
        Foo f;
        swap(f);
    }

    void swap(Foo & other) {
        std::swap(n_, other.n_);
        swap(str_, other.str_);
    }
};

void swap(Foo & one, Foo & other) {
    one.swap(other);
}

Поместите функцию подкачки в то же пространство имен, что и класс Foo. Поиск, зависящий от аргумента, находит его, когда пользователи делают вызов, чтобы поменять местами два Foo. Вы можете также реализовать operator = , используя свою функцию подкачки.

Кажется, это лучше, чем дублирование кода в конструкторе, но мне интересно, является ли *this = Foo() распространенным подходом при очистке объекта?

Очистка объекта не так уж и распространена:чаще всего либо объект (возможно, даже неизменяемый объект) создается и содержит реальные данные, либо не создается экземпляр.

Самый распространенный вид вещей, который являются сброс бы контейнеров...но сейчас вы не будете писать свои собственные классы-контейнеры, не так ли?

Есть ли какие-нибудь проблемы с этим ожиданием укусить меня за зад?

Да:

  • Если это на самом деле не Foo но вместо этого является DerivedFoo
  • Если Fooоператор присваивания не существует или содержит ошибки (например,если он не определен и оператор по умолчанию не подходит, например.поскольку элемент данных является голым указателем).

Есть ли другие лучшие способы добиться такого рода вещей?

Да, возможно, бесплатная функция была бы лучше (позволила бы избежать обеих вышеупомянутых проблем):

template<class T> void reconstruct(T* p)
{
    p->~T();
    new (p) T();
}

Да, это неэффективно с точки зрения производительности (создание другого объекта foo вместо того, чтобы работать на месте), и оно укусит вас, если вы выделите память в конструкторе с неприятной утечкой памяти.

Сделать его более безопасным с точки зрения памяти - это вызвать this-> gt; delete и this = new foo () - но это будет МЕДЛЕННО.

если вы хотите быть сверхбыстрым, создайте статическое пустое поле объекта и запишите его в поле «Сброс».

если вы хотите просто быстро назначить свойства одно за другим.

если вы хотите сохранить разумный стиль без дублирования, вызовите Reset from Ctor, как предложил Ates Goral, но вы потеряете более быструю конструкцию с параметрами по умолчанию.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top