c ++:new返回一个类型,但编译好

时间:2016-05-16 21:47:32

标签: c++

我正在测试一个C ++程序(演示装饰器设计模式),但发现了一个奇怪的问题。示例代码如下所示。错误行错误地有一个额外的new表达式,但令人惊讶的是代码编译并运行错误输出(装饰器输出两次)。

$ ./a.out
simple window with scroll bar with scroll bar

这里发生了什么?

#include <iostream>
#include <string>

using namespace std;

class Window {
public:
    virtual string desc() = 0;
    virtual ~Window() {}
};

class SimpleWindow : public Window {
public:
    string desc() { return "simple window"; }
};

class WindowDecorator : public Window {
protected:
    Window *window;

public:
    WindowDecorator(Window *window) : window(window) {}
};

class ScrollBar : public WindowDecorator {
public:
    ScrollBar(Window *window) : WindowDecorator(window) {}
    string desc() { return window->desc() + " with scroll bar"; }
};

int main()
{
    ScrollBar scrollBar = new ScrollBar(new SimpleWindow()); // error line
    cout << scrollBar.desc() << endl;

    return 0;
}

2 个答案:

答案 0 :(得分:3)

这里有一个例子,说明为什么我们在C ++中使用explicit关键字作为构造函数。 (见cppreference.com

你看,带有单个参数的构造函数,例如ScrollBar类中的参数......

ScrollBar(Window *window) : WindowDecorator(window) {}

...除非标记为explicit,编译器将使用它来执行隐式转换,在您的情况下,从Window *ScrollBar。这样的构造函数也称为转换构造函数。

那么,这一行会发生什么......

ScrollBar scrollBar = new ScrollBar(new SimpleWindow());

...编译器愉快地(隐式地)再次调用ScrollBar(Window *window)构造函数来转换ScrollBar *,从new ScrollBar()转到ScrollBar。这有效,因为:

  1. ScrollBarWindow的子类(通过WindowDecorator),因此ScrollBar *可以隐式转换为Window *
  2. 构造函数ScrollBar(Window* window)只有一个参数,标记为explicit,因此您从{{1}返回的指针中获取ScrollBar的实例}。
  3. 这很可能不是你在编写构造函数时想到的,所以你应该把它重写为:

    new ScrollBar()

    这会导致令人惊讶的行导致编译错误。 在C ++中,默认情况下始终使用explicit ScrollBar(Window *window) : WindowDecorator(window) {} 作为单参数构造函数通常是一个好主意,只有当您有意决定允许隐式转换语义时才将其删除。

答案 1 :(得分:1)

根据C ++标准(4.10指针转换)

  

3类型为“指向cv D的指针”的prvalue,其中D是类类型,可以是   转换为“指向cv B指针”类型的prvalue,其中B是基数   D ......的阶级(第10条)。

和(4次标准转换)

  

1标准转化为内置的隐式转化   含义。第4条列举了全套此类转换。一个   标准转换序列是一系列标准转换   以下顺序:

...

  

- 来自以下设置的零次或一次转化:整体促销,   浮点数提升,积分转换,浮点数   转换,浮点积分转换,指针转换,   指向成员转换的指​​针和布尔转换。

在本声明中

ScrollBar scrollBar = new ScrollBar(new SimpleWindow()); 

在右侧有一个派生类ScrollBar的指针,可以将其转换为基类Window的指针。

班级ScrollBar有转换构造函数

ScrollBar(Window *window) : WindowDecorator(window) {}

在上面的陈述中调用。