SomeValue所需的默认值

时间:2016-06-13 00:12:27

标签: delphi

当我创建以下#if OnWindows #define hidden_stdio_fp ((FILE * &)context->hidden.windowsio.h) #define hidden_stdio_autoclose ((SDL_bool &)context->hidden.windowsio.append) // ** Begin copied code ** static auto stdio_size = [](SDL_RWops * context) -> int64_t { int64_t pos, size; pos = SDL_RWseek(context, 0, RW_SEEK_CUR); if (pos < 0) { return -1; } size = SDL_RWseek(context, 0, RW_SEEK_END); SDL_RWseek(context, pos, RW_SEEK_SET); return size; }; static auto stdio_seek = [](SDL_RWops * context, int64_t offset, int whence) -> int64_t { #ifdef HAVE_FSEEKO64 if (std::fseeko64(hidden_stdio_fp, (off64_t)offset, whence) == 0) { return std::ftello64(hidden_stdio_fp); } #elif defined(HAVE_FSEEKO) if (std::fseeko(hidden_stdio_fp, (off_t)offset, whence) == 0) { return std::ftello(hidden_stdio_fp); } #elif defined(HAVE__FSEEKI64) if (std::_fseeki64(hidden_stdio_fp, offset, whence) == 0) { return std::_ftelli64(hidden_stdio_fp); } #else if (std::fseek(hidden_stdio_fp, offset, whence) == 0) { return std::ftell(hidden_stdio_fp); } #endif return SDL_Error(SDL_EFSEEK); }; static auto stdio_read = [](SDL_RWops * context, void *ptr, std::size_t size, std::size_t maxnum) -> std::size_t { std::size_t nread; nread = std::fread(ptr, size, maxnum, hidden_stdio_fp); if (nread == 0 && std::ferror(hidden_stdio_fp)) { SDL_Error(SDL_EFREAD); } return nread; }; static auto stdio_write = [](SDL_RWops * context, const void *ptr, std::size_t size, std::size_t num) -> std::size_t { std::size_t nwrote; nwrote = std::fwrite(ptr, size, num, hidden_stdio_fp); if (nwrote == 0 && std::ferror(hidden_stdio_fp)) { SDL_Error(SDL_EFWRITE); } return nwrote; }; static auto stdio_close = [](SDL_RWops * context) -> int { int status = 0; if (context) { if (hidden_stdio_autoclose) { /* WARNING: Check the return value here! */ if (std::fclose(hidden_stdio_fp) != 0) { status = SDL_Error(SDL_EFWRITE); } } SDL_FreeRW(context); } return status; }; static auto RWFromFP = [](FILE * fp, SDL_bool autoclose) -> SDL_RWops * { SDL_RWops *context = 0; context = SDL_AllocRW(); if (context != 0) { context->size = stdio_size; context->seek = stdio_seek; context->read = stdio_read; context->write = stdio_write; context->close = stdio_close; hidden_stdio_fp = fp; hidden_stdio_autoclose = autoclose; context->type = SDL_RWOPS_STDFILE; } return context; }; static auto SDL_RWFromFile = [](const char *file, const char *mode) -> SDL_RWops * { SDL_RWops *context = 0; if (!file || !*file || !mode || !*mode) { SDL_SetError("SDL_RWFromFile(): No file or no mode specified"); return 0; } FILE *fp = std::fopen(file, mode); if (fp == 0) { SDL_SetError("Couldn't open %s", file); } else { context = RWFromFP(fp, (SDL_bool)1); } return context; }; // ** End copied code ** #undef hidden_stdio_fp #undef hidden_stdio_autoclose #endif

时,我得到错误按摩Default value required for 'DefaultPort'
constructor

如何在不改变参数顺序的情况下解决问题(以下工作正常)。

type
  MyIdServer = class
  public
    constructor Create(const AOwner: TComponent = nil; const DefaultPort: Word; SilentExceptions: Boolean);
    destructor Destroy; override;
  end;

2 个答案:

答案 0 :(得分:6)

具有默认值的参数必须在参数列表中排在最后。如果任何参数具有默认值,则所有后续参数也必须具有默认值。

唯一的另一个选择是定义两个重载:

 constructor Create(const AOwner: TComponent; 
                    const DefaultPort: Word; 
                    SilentExceptions: Boolean); overload;
 constructor Create(const DefaultPort: Word; 
                    SilentExceptions: Boolean); overload;

并让第二个调用nil的{​​{1}}参数的第一个。

答案 1 :(得分:5)

基本问题是必须在任何非默认参数之后声明具有默认值的参数,其中您尝试将第一个参数设为默认值,然后是非默认参数。

但是,aOwner TComponent 参数的参与强烈暗示您的示例代码并不完全完整,因为这意味着您的服务器类将被实现为某些类的子类TComponent派生出超类。

如果是这种情况,那么我认为还有一个额外的考虑因素。

继承的构造函数

作为TComponent的子类,您的类将继承接受单个Owner参数的虚拟构造函数。

首先,这将导致警告,告诉您构造函数正在隐藏继承的构造函数。您可以向构造函数添加reintroduce指令以解决警告:

constructor Create(const AOwner: TComponent; const DefaultPort: Word; SilentExceptions: Boolean); reintroduce;

然而,这完全用你的新构造函数替换继承的虚拟构造函数,这可能不是你真正想要的。您还可以添加overload指令以保持对两者新构造函数和继承构造函数的访问权限:

constructor Create(const AOwner: TComponent; const DefaultPort: Word; SilentExceptions: Boolean); reintroduce; overload;

这解决了编译器投诉的简单事实,并确保虚拟构造函数保留在派生类上。

但它提出了一个问题:如果(或何时)使用了不允许指定这些值的继承构造函数,则应该为构造函数中提供的参数采用类的实例应该使用哪些值

如果使用类引用来实例化您的类,这仍然是可能的。 e.g。

var
  serverClass: TComponentClass;
  server: MyIdServer;
begin
  serverClass := MyIdServer;

  // .. elsewhere ...

  server := serverClass.Create(frmMain);
end;

如果您的组件是从表单资源流式传输的,只要这些附加参数设置的值也是已发布的属性,并且还设置了值,则很可能只会对您的类进行此类实例化。通过流媒体机制,可能没有任何问题。

但它确实提示了一个新的构造函数是否实际上是必需或需要的问题?

工厂方法替代

而不是重载构造函数,而是在类上提供工厂方法可能更合适:

MyIdServer = class(TComponent)
public
  class function NewServerWithDefaults(DefaultPort: Word; SilentExceptions: Boolean): MyIdServer;
end;

此工厂方法的实现将使用 NIL 所有者参数调用相应的构造函数,然后初始化传递的参数中指定的所需属性:

class function MyIdServer.NewServerWithDefaults(DefaultPort: Word; SilentExceptions: Boolean): MyIdServer;
begin
  result := MyIdServer.Create(NIL);

  // Assuming the properties involved here but you get the idea...

  result.DefaultPort      := DefaultPort;
  result.SilentExceptions := SilentExceptions;
end;

你可以说这是&#34;天使在Pin Head上跳舞&#34;因为最终结果是相同的,就代码的行为而言。不同之处在于最终的类设计本身。

这是否比简单地引入新的构造函数更合适或更合适取决于您。 :)