Как использовать конструкторов базового класса и оператор присваивания в C++?

В C++ 11 можно использовать std::to_string :

#include 

std::string s = std::to_string(5);

, Если Вы работаете с до C++ 11, Вы могли бы использовать потоки C++:

#include 

int i = 5;
std::string s;
std::stringstream out;
out << i;
s = out.str();

Взятый от http://notfaq.wordpress.com/2006/08/30/c-convert-int-to-string/

92
задан Azeem 20 April 2018 в 10:25
поделиться

4 ответа

Вы можете явно вызывать конструкторы и операторы присваивания:

class Base {
//...
public:
    Base(const Base&) { /*...*/ }
    Base& operator=(const Base&) { /*...*/ }
};

class Derived : public Base
{
    int additional_;
public:
    Derived(const Derived& d)
        : Base(d) // dispatch to base copy constructor
        , additional_(d.additional_)
    {
    }

    Derived& operator=(const Derived& d)
    {
        Base::operator=(d);
        additional_ = d.additional_;
        return *this;
    }
};

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

class ImplicitBase { 
    int value_; 
    // No operator=() defined
};

class Derived : public ImplicitBase {
    const char* name_;
public:
    Derived& operator=(const Derived& d)
    {
         ImplicitBase::operator=(d); // Call compiler generated operator=
         name_ = strdup(d.name_);
         return *this;
    }
};  
119
ответ дан 24 November 2019 в 06:33
поделиться

Скорее всего, у вас есть недостаток в вашем дизайне (подсказка: нарезка , семантика сущностей vs семантика значений ). Наличие полной семантики копий / значений для объекта из полиморфной иерархии часто вовсе не требуется. Если вы хотите предоставить его на случай, если он понадобится вам позже, значит, он вам никогда не понадобится. Вместо этого сделайте базовый класс не копируемым (например, наследуя от boost :: noncopyable), и все.

Единственное правильное решение, когда такая потребность действительно появляется, - это идиома envelop-letter. , или небольшой фреймворк из статьи о Обычных объектах Шона Пэрента и Александра Степанова IIRC. Все остальные решения дадут вам проблемы с нарезкой и / или LSP.

По этому поводу,

4
ответ дан 24 November 2019 в 06:33
поделиться

Краткий ответ: Да, вам нужно будет повторить работу в D

Длинный ответ:

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

Но если ваш класс 'D' содержит ресурсы, вам нужно будет проделать некоторую работу.

Я нашел вашу копию Конструктор немного странный:

B(const B& b){(*this) = b;}

D(const D& d){(*this) = d;}

Обычно копируют цепочку конструкторов, так что они создаются копией, начиная с основания. Здесь, поскольку вы вызываете оператор присваивания, конструктор копирования должен вызывать конструктор по умолчанию, чтобы по умолчанию инициализировать объект сначала снизу вверх. Затем вы снова спускаетесь, используя оператор присваивания. Это кажется довольно неэффективным.

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

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

class X
{
    // If your class has no resources then use the default version.
    // Dynamically allocated memory is a resource.
    // If any members have a constructor that throws then you will need to
    // write your owen version of these to make it exception safe.


    X(X const& copy)
      // Do most of the work here in the initializer list
    { /* Do some Work Here */}

    X& operator=(X const& copy)
    {
        X tmp(copy);      // All resource all allocation happens here.
                          // If this fails the copy will throw an exception 
                          // and 'this' object is unaffected by the exception.
        swap(tmp);
        return *this;
    }
    // swap is usually trivial to implement
    // and you should easily be able to provide the no-throw guarantee.
    void swap(X& s) throws()
    {
        /* Swap all members */
    }
};

Даже если вы унаследовали класс D от X, это не повлияет на этот шаблон.
По общему признанию, вам нужно немного повторить работу, сделав явные вызовы базового класса, но это относительно тривиально.

class D: public X
{

    // Note:
    // If D contains no members and only a new version of foo()
    // Then the default version of these will work fine.

    D(D const& copy)
      :X(copy)  // Chain X's copy constructor
      // Do most of D's work here in the initializer list
    { /* More here */}



    D& operator=(D const& copy)
    {
        D tmp(copy);      // All resource all allocation happens here.
                          // If this fails the copy will throw an exception 
                          // and 'this' object is unaffected by the exception.
        swap(tmp);
        return *this;
    }
    // swap is usually trivial to implement
    // and you should easily be able to provide the no-throw guarantee.
    void swap(D& s) throws()
    {
        X::swap(s); // swap the base class members
        /* Swap all D members */
    }
};
17
ответ дан 24 November 2019 в 06:33
поделиться

Вам нужно будет переопределить все конструкторы, которые не являются конструкторами по умолчанию или копиями . Вам не нужно переопределять конструктор копирования или оператор присваивания, поскольку те, которые предоставлены компилятором (в соответствии со стандартом), будут вызывать все версии базы:

struct base
{
   base() { std::cout << "base()" << std::endl; }
   base( base const & ) { std::cout << "base(base const &)" << std::endl; }
   base& operator=( base const & ) { std::cout << "base::=" << std::endl; }
};
struct derived : public base
{
   // compiler will generate:
   // derived() : base() {}
   // derived( derived const & d ) : base( d ) {}
   // derived& operator=( derived const & rhs ) {
   //    base::operator=( rhs );
   //    return *this;
   // }
};
int main()
{
   derived d1;      // will printout base()
   derived d2 = d1; // will printout base(base const &)
   d2 = d1;         // will printout base::=
}

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

2
ответ дан 24 November 2019 в 06:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: