如何在C ++中使用基类的构造函数和赋值运算符?

我有一组构造函数和一个赋值运算符。

class B { public: B(); B(const string & s); B(const B & b){(*this) = b;}; B & operator= (const B & b); private: virtual void foo(); // and other private member variables and functions } 

我想创build一个只会覆盖函数foo()的inheritance类D,不需要其他更改。

但我想要D具有相同的一组构造函数,包括复制构造函数和赋值运算符为B:

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

我是否必须在D中重写它们,还是有办法使用B的构造函数和操作符? 我特别想避免重写赋值运算符,因为它必须访问所有B的私有成员variables。

您可以显式调用构造函数和赋值操作符:

 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; } }; 

简答:是的,您需要重复D中的工作

很长的回答:

如果派生类“D”不包含新的成员variables,那么默认版本(由编译器生成的应该可以正常工作)。 默认的Copy构造函数将调用父级拷贝构造函数,默认赋值操作符将调用父级赋值操作符。

但是,如果你的class级“D”包含资源,那么你将需要做一些工作。

我发现你的复制构造有点奇怪:

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

通常复制构造函数链,使它们从底部复制构build。 在这里,因为您正在调用赋值运算符,所以复制构造函数必须调用默认的构造函数,以便从下往上初始化对象。 然后你再次使用赋值操作符下去。 这似乎相当低效。

现在如果你做了一个任务,你是从下到上(或者从上到下)复制的,但是看起来你很难这么做,并提供了一个强有力的例外保证。 如果在任何时候一个资源不能复制,你抛出一个exception对象将处于不确定的状态(这是一件坏事)。

通常情况下,我已经看到它做了相反的事情。
赋值操作符是根据拷贝构造函数和交换来定义的。 这是因为它更容易提供强有力的例外保证。 我不认为你可以通过这种方式提供强有力的保证(我可能是错的)。

 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 */ } }; 

即使你从X派生出一个类D,这也不会影响这个模式。
无可否认,你需要重复一些工作,对基类进行明确的调用,但这是相对微不足道的。

 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 */ } }; 

你最有可能在你的devise中有一个缺陷(提示: 切片实体语义值语义 )。 从多态层次结构对对象有一个完整的复制/ 值语义通常是不需要的。 如果你想提供它,以防万一以后可能需要它,这意味着你永远不会需要它。 让基类不可复制(例如通过从boost :: noncopyableinheritance),这就是全部。

唯一正确的解决scheme就是信封成语 ,也就是Sean Parent和Alexander Stepanov IIRC关于Regular Objects的文章中的小框架。 所有其他的解决scheme将给你切片,和/或LSP的麻烦。

你将不得不重新定义所有不是默认复制构造函数的构造函数。 您不需要重新定义复制构造函数或赋值运算符(由编译器提供)(根据标准)将调用所有基本版本:

 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指出的那样,如果您定义了任何构造函数,编译器将不会为您生成默认的构造函数,并且包含复制构造函数。

原始代码是错误的:

 class B { public: B(const B& b){(*this) = b;} // copy constructor in function of the copy assignment B& operator= (const B& b); // copy assignment private: // private member variables and functions }; 

一般来说,您不能根据复制分配来定义复制构造函数,因为复制分配必须释放资源,并且复制构造函数不会!

要了解这一点,请考虑:

 class B { public: B(Other& ot) : ot_p(new Other(ot)) {} B(const B& b) {ot_p = new Other(*b.ot_p);} B& operator= (const B& b); private: Other* ot_p; }; 

为避免内存泄漏,首先必须删除ot_p指向的内存:

 B::B& operator= (const B& b) { delete(ot_p); // <-- This line is the difference between copy constructor and assignment. ot_p = new Other(*b.ot_p); } void f(Other& ot, B& b) { B b1(ot); // Here b1 is constructed requesting memory with new b1 = b; // The internal memory used in b1.op_t MUST be deleted first !!! } 

所以,拷贝构造函数和拷贝赋值是不同的,因为前者构造和对象到初始化的内存中,而后者必须在构造新对象之前首先释放现有的内存。

如果您按照本文中的build议进行操作:

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

你将会删除一个没有记忆的记忆。