What is the differenece between Shape s = new Circle(); and



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

sShape * s = new Circle();这类形状类型的指针有什么区别?

这两句话截然不同,我先说简单(而且正确)的那句:

<标题> 的指针
Shape * s = new Circle();

正如您已经提到的,s在本例中是指向- Shape指针。语句声明了s,并给它赋值了一个Circle类型的指针。这意味着Circle是从Shape派生出来的。

因为c++是一种多态语言,s现在指向Circle类型的对象。如果Shape上的虚函数被Circle重载,那么对s上虚函数的调用将被重定向到重载的Circle上。

一个简单的和不完整的示例(注意在c++中,没有按关键字定义的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++中面向对象编程的基石之一。还要注意的是,override关键字并不是完全必要的,我个人认为这是一个很好的风格。

<标题>
Shape s = new Circle();

这个语句看起来和第一个语句差不多,但是做的事情完全不同。首先,s现在是实际的对象,这是好的和坏的同时。这很好,因为只要当前复合语句离开,它就会自动销毁,释放它占用的所有内存。

但这很糟糕,因为s不能再多态了:它被声明为Shape -Object,没有别的了。使用上面的代码,并且您的前提是Shape是一个抽象类,这甚至无法工作。抽象类不能被实例化,因为它们有未实现的函数。

如果我们假设它不是抽象的,我们将陷入许多隐式转换漏洞,我只想简短地描述一下:

为了使上面的语句是正确的c++代码,Shape需要有一个构造函数,该构造函数接受一个类型为Circle*的参数,即而不是声明为explicit。这将使Shape s = new Circle();等同于Shape s = Shape(new Circle());。执行隐式转换。

这是, 非常坏代码!new Circle()Circle对象分配的内存永远不会被释放,因为它没有名字!这被称为内存泄漏,这就是为什么你几乎不应该在c++中使用裸指针的原因。另一种方法叫做智能指针,你应该尽可能地使用它们。

此外,为了防止类似的事情发生,您应该声明所有的构造函数explicit(或至少只接受一个参数的构造函数):
explicit Shape(Circle *);

最后,我想鼓励你至少读一本,最好是几本关于c++的书。我刚才解释的都是你应该知道的基本知识,这里不是学习的好地方。c++是一门危险的语言,你可以做很多非常错误的事情,即使它们最初看起来很正常。

new Circle创建Circle类型的动态对象并给出指向该对象的指针。该指针可用于初始化指向CircleCircle的任何基类的指针;所以假设Circle来源于Shape,第二种形式

Shape * s = new Circle;

给出一个(静态)类型的指针Shape*,指向一个(动态)类型的对象Circle。这可以多态地使用,通过Shape定义的抽象接口与Circle交互,而不需要知道实际类型是Circle

第一个错误的例子

Shape s = new Circle;

尝试创建一个类型为Shape的对象,用指向Circle的指针初始化。由于是抽象的,Shape不能被实例化,除非作为派生类的一部分,所以编译失败。

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

最新更新