Shape s = new Circle()之间有什么区别;和Shape * s = new Circle();

时间:2014-06-11 11:59:35

标签: c++ oop

这是一个面试问题,我们写Shape s = new Circle(); Shapeabstract class

s与形状类型指针Shape * s = new Circle();之间有什么区别?

2 个答案:

答案 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类型的动态对象,并给出一个指向它的指针。该指针可用于初始化指向CircleCircle的任何基类的指针;假设Circle来自Shape,第二种形式

Shape * s = new Circle;

给出(静态)类型Shape*的指针,指向(动态)类型Circle的对象。这可以通过多态进行,通过Circle定义的抽象接口与Shape进行交互,而无需知道实际类型为Circle

第一个不正确的例子

Shape s = new Circle;

尝试创建类型为Shape的对象,并使用指向Circle的指针进行初始化。作为抽象,Shape不能被实例化,除非作为派生类的一部分,因此无法编译。

当您使用new时,请务必记住delete动态对象完成后的动态对象,或者更好的是,使用智能指针等RAII类来处理那容易出错的单调乏味。此外,请确保Shape具有虚拟析构函数,以便可以安全地删除其派生类型。