我正在测试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;
}

最佳答案

这里的示例说明了为什么我们在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转换。之所以有效,是因为:


ScrollBarWindow的子类(通过WindowDecorator),因此ScrollBar *可以隐式转换为Window *
构造函数ScrollBar(Window* window)具有单个参数,并且未标记为explicit,因此您可以从ScrollBar返回的指针中获取new ScrollBar()的实例。




这很可能不是您编写构造函数时所想到的,因此您应该将其重写为:

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


这将导致意外的行导致编译错误。
在C ++中,通常总是对默认的单参数构造函数使用explicit是一个好主意,仅当您有意决定允许隐式转换语义时才将其删除。

关于c++ - C++:new返回一个类型,但是可以编译,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37263794/

10-12 20:40