这是一个面试问题,我们写Shape s = new Circle();
Shape
是abstract class
。
s
与形状类型指针Shape * s = new Circle();
之间有什么区别?
答案 0 :(得分:2)
这两个陈述彼此截然不同,我先用简单的(正确的)陈述:
Shape * s = new Circle();
正如您已经提到的,在这种情况下,s
是指向 - Shape
的指针。该语句声明s
,并为其指定类型为Circle
的指针。这意味着Circle
派生自Shape
。
因为C ++是一种多态语言,s
现在指向类型为Circle
的对象。如果Shape
的{{3}}被Circle
重载,则s
上对这些函数的调用将被重定向到重载Circle
实现。
一个简单的和不完整的示例(请注意,在C ++中,有virtual functions abstract classes
):
class Shape{
virtual int getX() = 0; //getX() is purely virtual, so Shape is abstract
}
class Circle : public Shape{
int getX() override;
}
Shape * s = new Circle();
int myX = s->getX(); //will call Circle::getX()
这是基本的多态行为,是C ++中面向对象编程的基石之一。另请注意,no by-keyword defined并非完全必要,我个人认为它是一种好的风格。
Shape s = new Circle();
这个陈述看起来几乎和第一个一样,但做了一些完全不同的事情。首先,s
现在是实际的对象,同时也是好的和坏的。这很好,因为只要剩下当前的override keyword,就会自动销毁它,释放掉它占用的所有内存。
但这很糟糕,因为s
不再是多态的:它被声明为Shape
- 对象,而不是其他任何东西。使用上面的代码,以及Shape
是一个抽象类的前提,这甚至都行不通。抽象类无法实例化,因为它们具有未实现的函数。
如果我们认为它不是抽象的,我们会陷入许多隐含的转换漏洞,我只想以简短的方式描述:
为了使上述语句为正确的C ++代码,Shape
需要一个构造函数,该构造函数接受一个Circle*
类型的参数,即 not 声明为explicit
。这会使Shape s = new Circle();
等同于Shape s = Shape(new Circle());
。发生了compound statement。
这是非常, 非常 错误代码! new Circle()
为永远无法释放的Circle
对象分配内存,因为它没有名称!这称为内存泄漏,这就是为什么你几乎永远不会在C ++中使用裸指针的原因。替代方案称为implicit conversion,您应尽可能使用它们。
此外,为了防止这样的事情发生,你应该声明所有的构造函数explicit
(或者至少只有一个参数的构造函数):
explicit Shape(Circle *);
最后,我想鼓励你smart pointers。我刚才解释的一切都是你应该真正了解的超级基本内容,而这并不是学习它的最佳场所。 C ++是一种危险的语言,你可以做很多非常错误的事情,即使它们似乎最初都有用。
答案 1 :(得分:1)
new Circle
创建一个Circle
类型的动态对象,并给出一个指向它的指针。该指针可用于初始化指向Circle
或Circle
的任何基类的指针;假设Circle
来自Shape
,第二种形式
Shape * s = new Circle;
给出(静态)类型Shape*
的指针,指向(动态)类型Circle
的对象。这可以通过多态进行,通过Circle
定义的抽象接口与Shape
进行交互,而无需知道实际类型为Circle
。
第一个不正确的例子
Shape s = new Circle;
尝试创建类型为Shape
的对象,并使用指向Circle
的指针进行初始化。作为抽象,Shape
不能被实例化,除非作为派生类的一部分,因此无法编译。
当您使用new
时,请务必记住delete
动态对象完成后的动态对象,或者更好的是,使用智能指针等RAII类来处理那容易出错的单调乏味。此外,请确保Shape
具有虚拟析构函数,以便可以安全地删除其派生类型。