【问题标题】:Copy Constructor, Assignment operator overloading复制构造函数、赋值运算符重载
【发布时间】:2014-10-12 04:01:26
【问题描述】:

现在我不需要重载赋值参数或编写复制构造函数 (至少,我似乎从来不用,因为我从来没有遇到过问题)

据我所知,当我想在初始化后将一个对象及其值复制到另一个对象时,必须重载赋值运算符(与 CopyConstructor 相同,但在初始化时)。

Object a;
Object b;

// do something with a
b = a; //for this line, the Assignment Parameter must be overloaded (nearly never do such things)
Object c = a; // needs a Copy constructor 

但如果我这样写:

Object a;
Object* b;

b = &a; //I think I won't need one here since b actually points to Object a
Object* c = a; // I think same here

但是,如果我将一个对象复制到它自己的类/父类之外,我该怎么办。

class MyOtherClass{Object obj..........}

void MyOtherClass::SetObject(Object obj)
{
  this->obj = obj; // Assignment operator overloading needed in class Object?
}

我必须重载 Object 类中的赋值运算符吗? 如果我执行以下操作会怎样:

void MyOtherClass::SetObject(Object &obj)
{
  this->obj = obj; // Assignment operator overloading needed in class Object?
}

最后一个问题,这是否也包括Enums?(我认为是的)

void MyOtherClass::SetObject(ENumClass Eobj)
.
.

【问题讨论】:

    标签: c++ object instance copy-constructor assignment-operator


    【解决方案1】:

    关于是否实现赋值运算符的问题,答案是——视情况而定。

    请记住,如果您自己不提供一个默认赋值运算符(在 C++11 中,如果您没有显式禁用默认运算符),编译器将生成它自己的默认赋值运算符。默认运算符使用自己的赋值运算符对每个单独的类成员进行赋值。

    只要您的班级成员本身是赋值安全的,那么默认运算符就足够了,您不需要提供自己的运算符。当您需要执行自定义操作时,您只需提供自己的操作符,例如深复制动态分配的成员、调试日志记录、输入验证等。

    【讨论】:

    • 所以对于我的 Setter 方法我不需要重载它?
    【解决方案2】:

    为什么你使用一个单独的方法 SetObject() 复制 operator=() 语义?

    为了正确地将基类分配给派生类,您可以通过这种方式在派生对象中重载 operator=()

    DerivedObj& DerivedObj::operator=(const BaseObj& rhs){
        // call parent operator= in functional form
        BaseObj::operator=(rhs);
        return *this;
    }
    

    当然你应该实现

    BaseObj& BaseObj::operator=(const BaseObj& rhs)
    

    但是,如果您需要将基类分配给派生类,我不会说这是一个好的设计

    【讨论】:

    • 你为什么使用一个单独的方法 SetObject() 复制 operator=() 语义?你这句话是什么意思?实际上我不想将基类分配给派生类。 MyOtherClass 应该只有一个 setter 方法来获取 Object obj。示例:int main() { Object obj; //do things with obh MyOtherClass other; other.SetObject(obj); } //不知何故 code 格式在这里看起来很糟糕,对不起!
    • 我不认为这是“当然”。 BaseObj::operator=(const BaseObj&) 可能并不总是有意义。一般来说,我会从基类中省略 operator=() 。 (在一个好的设计中,基类无论如何都没有状态,所以这样的赋值运算符无论如何都不会做任何事情,而且就 API 而言,承诺在基类级别进行赋值是灾难的根源)。
    • @MichaelAaronSafyan 同意,但一般来说,目标对象的基类(我们为其实现 operator=())可以派生为其他对象(并且可以具有状态、公共方法等)
    • 人们希望通过组合而不是进一步继承来重用提供实现而不仅仅是接口的实现类。
    • @Sleicreider 你的意思是 SetObject(obj) 重置你班级的状态吗?如果是这样,您可以从 Object 构造新的 MyOtherClass 并将其与您的对象交换。
    【解决方案3】:

    编写一个从基类型赋值的赋值运算符是合法的,编写一个从相同类型的具体对象赋值的赋值运算符也是合法的。然而,即使两者都是合法的,通常最好的设计是从相同类型的对象或从其他具体类型(这种赋值实际上有意义)而不是从通用类型实现赋值。

    话虽如此,它确实因应用程序而异。更具体一点,请考虑:

                         Shape
            /             |                \
          Triangle      Quadrilateral      Circle
    

    ... 在 Circle 中有一个可以从任何 Shape 进行赋值的赋值运算符可能没有意义。这种方法甚至会做什么?这些不同的实现没有任何共同之处。

    现在,或者,考虑:

                         Point2D
                         /     \
              CartesianCoord   PolarCoord
    

    ... 在这样的应用程序中,提供一个接受通用 Point2D 的赋值运算符以促进两种类型之间的转换可能是有意义的。在这种情况下,所有实现都表示相同的信息,并且可以在表示之间进行非破坏性转换。因此,在这种情况下,允许进行更一般的转换是有意义的。

    所以,真的,这一切都与概念上的意义有关。如果您可以以不破坏信息的方式向对象分配/从对象分配,那是理智的;如果您的作业具有破坏性,则可能是糟糕的设计。

    对于参数,通常使用“const T&”(其中“T”是您要分配的类型)或“T&&”在破坏性分配的情况下。赋值的返回类型应该是对与当前对象相同类型的对象的引用。换句话说:

    class CartesianCoord : public Point2D {
     public:
       // ...
       CartesianCoord& operator=(const Point2D&);
    };
    

    或者

    class CartesianCoord : public Point2D {
     public:
       // ...
       CartesianCoord& operator=(const Point2D&);
       CartesianCoord& operator=(CartesianCoord&&); // fast destructive assignment
    };
    

    或者

    class CartesianCoord : public Point2D {
     public:
       // ...
       CartesianCoord& operator=(const CartesianCoord&);  // if conversion disallowed
       CartesianCoord& operator=(CartesianCoord&&); // fast destructive assignment
    };
    

    【讨论】:

    • 什么是具体对象?为什么要谈论继承?
    • 一个具体对象是一个实现了所有方法的对象(没有纯虚方法)。谈论继承是因为问题似乎是在询问在继承的情况下赋值运算符的参数应该是什么。
    • 你能有一个非具体的对象吗?
    • 你不能实例化一个非具体的对象,但是当涉及到声明和类型时,是的,你可以有一个非具体的对象。
    • 不,一个对象就是一个实例。你不能有一个非具体的对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-23
    • 1970-01-01
    • 1970-01-01
    • 2011-07-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多