【问题标题】:Default parameters and forward declaration默认参数和前向声明
【发布时间】:2021-10-07 09:28:31
【问题描述】:

我有一个类Property,在文件property.h 中有一个我想要默认参数的构造函数:

class Property {
  Property(OtherClass* value = myApp->someValue) {...};
};

其中myApp,另一个类型Application,在另一个文件中定义,该文件广泛使用了这个Property 类。由于这个其他文件#includes property.h,当然,我不能在property.h中#include这个文件

property.h 不知道myApp 也不知道Application(尽管property.cpp 知道,并且OtherClassproperty.h 中已知)。 我可以转发声明OtherClass 并将myApp 声明为extern,但这会导致错误C2027“使用未定义类型的应用程序”,正如预期的那样:

class Application;
extern Application* myApp;
class Property {
  Property(OtherClass* value = myApp->someValue) {...};
};

解决方案可能是在 .cpp 文件中使用默认参数,但不建议这样做 (here)。

我怎样才能让这个默认参数工作? (即,没有另一种解决方案涉及没有默认参数告诉我默认参数是邪恶的,或者我的设计一开始就很差等;))。谢谢!

【问题讨论】:

  • 大多数问题都可以通过另一个层次的间接来解决。进行一个在 .cc 中实现的函数调用,其中 Application 是已知的。 (尝试制作一个 MCVE 来检查一下...)

标签: c++ forward-declaration default-arguments


【解决方案1】:

值得注意的是,函数参数的默认参数是在调用函数的地方解析的(与定义函数的地方相反)。这为yet another level of indirection 解决 OPs 问题提供了必要的空间:

没有访问myApp->someValue,而是引入了一个辅助函数,它可以在Property的定义之前声明,但在Application完全定义之后实现。 (这也可以是Propertystatic 成员函数。)

举例说明:

#include <iostream>

int getAppDefault();

struct Application;
extern Application *pApp;

struct Property {
  int value;
  Property(int value = getAppDefault()): value(value) { }
};

struct Application {
  int valueDefault = 123;
};

Application app;
Application *pApp = &app;

int getAppDefault() { return pApp->valueDefault; }

int main()
{
  Property prop1;
  std::cout << "prop1: " << prop1.value << '\n';
  app.valueDefault = 234;
  Property prop2;
  std::cout << "prop2: " << prop2.value << '\n';
}

输出:

prop1: 123
prop2: 234

Demo on coliru

为了强调value = getAppDefault()实际上是在调用构造函数Property::Property()时解决的,我在默认构造prop2之前修改了Application::valueDefault


当然,如果Property 存储的是指针而不是值本身,这也可以:

Demo on coliru

【讨论】:

  • 我正在验证这个答案,尽管我最终使用了一个更简单的选项(默认为 NULL,然后在构造函数中移动到 cpp 文件,测试 NULL 并改用 myApp->someValue)。但是在我的情况下,另一个拥有另一个空构造函数的选项不会那么容易,因为我有其他具有默认值的参数。谢谢!
【解决方案2】:

首先,要使用Application 类的成员,您必须拥有Application 类的完整定义。否则编译器将不知道该类的成员。

其次,您可能应该使Property 构造函数explicit 禁止来自Application 的隐式转换。这种隐式转换往往会使代码更难阅读、理解和维护。

第三,作为解决第一个问题的一种可能方法,您可以使用 重载 来拥有两个 Property 构造函数:一个带有非默认 Property* 参数,一个没有任何论点。无参数构造函数委托给单参数构造函数进行初始化。这种拆分为两个构造函数将更容易定义(实现)与声明分开。


在代码中,我的解决方案如下所示:

首先是Property 头文件

// Forward declaration only, no header file needed
class OtherClass;

class Property
{
public:
    Property();
    explicit Property(OtherClass* other);

    // ...
};

然后是Property源码文件

#include "Property.h"

// Get the full and complete definition of the Application class
#include "Application.h"

extern Application* myApp;

// Will probably need the full definition of the OtherClass as well
#include "OtherClass.h"

// Default constructor delegates to one-argument constructor
Property::Property()
    : Property{ myApp->GetOtherObject() }
{
}

Property::Property(OtherClass* other)
{
    // Use the other class here...
}

【讨论】:

    猜你喜欢
    • 2010-12-20
    • 2021-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多