当我创建以下#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;
答案 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;因为最终结果是相同的,就代码的行为而言。不同之处在于最终的类设计本身。
这是否比简单地引入新的构造函数更合适或更合适取决于您。 :)