我有这样的代码:
class Base
{
public:
void operator = (const Base& base_)
{
}
};
class Child : public Base
{
public:
};
void func()
{
const Base base;
Child child;
child = base;
}
我的问题是:既然Child派生自Base(因此它应该继承Base的operator =),那么语句怎么来的
child = base;
被执行,我得到一个像这样的编译器错误:
>.\main.cpp(78) : error C2679: binary '=' : no operator found which takes a right-hand operand of type 'const Base' (or there is no acceptable conversion)
1> .\main.cpp(69): could be 'Child &Child::operator =(const Child &)'
1> while trying to match the argument list '(Child, const Base)'
我想要的行为是让Child类认识到它被分配了一个Base类,并且只是“自动”调用它的父类的operator =。
我将此代码添加到Child类
void operator = (const Base& base_)
{
Base::operator=(base_);
}
然后一切都编好了。虽然我不认为这会很好,因为如果我有5个不同的类继承自Base,那么我必须在每个派生类中重复相同的代码。
注意:我将Base
复制到Child
的意图是简单地将 common 的成员复制到Base
}和Child
(这将是Base
的所有成员)。即使在阅读了下面的所有答案之后,我也真的不明白为什么C ++不允许这样做,特别是如果在operator=
类中定义了明确的Base
。
答案 0 :(得分:13)
该标准在12.8 / 10“复制类对象”中提供了您的具体问题的原因(强调添加):
因为如果没有由用户声明,为类隐式声明了复制赋值运算符,基类复制赋值运算符总是被派生类的复制赋值运算符隐藏(13.5.3 )。
因此,当编译器对operator=(const Child&)
执行名称查找/重载解析时,会隐式声明Child::operator=()
,所以Base
类的函数签名从未被考虑过(隐藏)
答案 1 :(得分:8)
下面的代码是我从一开始就想要的行为,它编译,
class Base
{
public:
void operator = ( const Base& base_)
{
}
};
class Child : public Base
{
};
void func()
{
const Base base;
Child child;
child.Base::operator=(base);
}
我从来不知道你可以明确地称之为:
child.Base::operator=(base);
无论如何,我学到了很多东西。感谢所有在此发布答案的人。
答案 2 :(得分:6)
您不能将Base指定给Child,因为Child可能不是base。要使用典型的动物示例,您在此处拥有:
const Animal animal;
Dog dog;
dog = animal;
但是动物可能是一只猫,你肯定不能把猫分配给狗。
话虽如此,你不可能反过来这样做:
const Dog dog;
Animal animal;
animal = dog;
狗肯定是一种动物,但你有一个尺寸问题。 animal
占用了sizeof(Animal)
空间,但dog
占用sizeof(Dog)
空间,这两个数量可能不同。
在C ++中使用多态时,您需要使用引用或指针。例如,以下情况很好:
Dog* const dog;
Animal* animal;
animal = dog;
这里,无论指向什么,指针的大小都是相同的,因此可以进行分配。请注意,您仍需要维护正确的层次结构转换。即使使用指针,也不能将Base指定给Child,因为我之前解释的原因是:Base可能与Child完全不同。
答案 3 :(得分:2)
因为基地不是孩子。考虑:
class Child : public Base
{
public:
SomeFunc();
};
void func()
{
const Base base;
Child child;
child = base;
child.SomeFunc(); // whatcha gonna call?
}
更新:要回答评论中的问题,child.SomeFunc();
完全合法 - 如果child
的值不是真正的Child对象,那么这只是一个问题。编译器不允许child = base;
,因为它会造成合法调用失败的情况。
答案 4 :(得分:2)
这实际上不是一个答案,但是如何回答问题。这就是为什么 - 不是。
类应该意味着什么。关于类中任何格式良好的对象(“类不变量”),应该有一些有用的东西,因为这样你可以根据类的工作原理来推理程序。如果一个类只是数据成员的集合,则不能这样做,并且必须根据每个单独的数据元素来推断该程序。
建议的赋值运算符将更改基类中的每个数据成员,但不会触及子类中定义的成员。这意味着两件事之一。
如果在将另一个Base对象转储到其Base组件后,Child对象格式正确,则意味着其他数据成员与Base数据成员没有真正的连接。在这种情况下,Child实际上不是Base的子类型,公共继承是错误的关系。儿童与基地没有“是一种”关系,而是与“有一种”关系。这通常由组合表示(Child具有Base数据成员),也可以由私有继承表示。
如果在将另一个Base对象转储到其Base组件后,Child对象的格式不正确,那么建议的赋值运算符是一种非常快速且方便的方法来处理变量。
举个例子,让我们有一个哺乳动物基类和两个子类,Cetacean和Bat。动物有大小和体重之类的东西,而儿童班则有与他们吃什么以及如何移动有关的领域。我们将鲸类分配给哺乳动物,这是合理的(所有鲸类特定的领域都被丢弃),现在我们将该哺乳动物分配给蝙蝠。突然间,我们有一个二十吨的蝙蝠。
使用一组设计良好的类,我能想到的这个赋值运算符的唯一方法是,如果子类没有其他数据成员,只有其他行为,即使这样也是可疑的。
因此,我唯一的建议是重做你的班级设计。许多人,特别是那些熟悉Java和其他倾向于深度继承层次结构的语言的人,在C ++中使用的继承太多了。 C ++通常最适用于大量基类和相对较浅的层次结构。
答案 5 :(得分:1)
如果未明确声明复制赋值,C ++编译器将为您创建复制赋值。在你的情况下,它将是
class Child : public Base
{
public:
Child& operator=(const Child& rhs) { ... }
};
这就是你得到这个编译错误的原因。此外,当您提供复制赋值时,返回对自身的引用更有意义,因为它允许您将操作符链接在一起。
宣布像这样的副本分配不是一个好主意。
void operator = (const Base& base_)
{
Base::operator=(base_);
}
我不明白你为什么只想在复制作业时复制基类部分。
答案 6 :(得分:1)
答案比我想象的更可怕。 你可以这样做。标准的平均值在7.3.3节中有一点注意 主要问题是在Child中默认定义的operator =隐藏了operator = from Base,因此你需要使用“using”将它导回到类范围。
这对我编译好。但是我发现这个想法非常可怕,如果Child中的内容需要初始化,可能会充满未定义的行为。
class Base
{
public:
void operator = (const Base& base_)
{
}
};
class Child : public Base
{
public:
using Base::operator=;
};
int main()
{
const Base base = Base();
Child child;
child = base;
}
编辑:再次制作基本const并避免“未初始化的const”错误。
答案 7 :(得分:0)
问题是您无法为非常量变量分配常量值。如果可以,您可以通过修改非常量变量来修改常量值。
基础是const是必要的吗?我问,因为你真的遇到了两个问题:const正确性和继承性/多态性。
编辑:我现在第二次猜测自己的第一个声明。有任何澄清吗?