【问题标题】:Relaying private arguments to derived-class virtual method将私有参数中继到派生类虚拟方法
【发布时间】:2016-01-28 16:40:59
【问题描述】:

考虑以下两个类:

class Parent
{
private:
    char outputBuffer[100];

protected:
    virtual void WriteOutput() = 0;
};


class Child : public Parent
{
private:
    double value1;
    double value2;
    void WriteOutput()
    {
        sprintf(outputBuffer, "%.2f %.2f", value1, value2);
    }
};

父级定义了一个virtual 函数,然后在子级中实现该函数,其目的是将格式化的字符串写入输出缓冲区。

我的挑战是我不希望输出缓冲区对子类直接可见。换句话说,我希望 WriteOutput 看起来像:

void WriteOutput()
{
    myFcn("%.2f %.2f", value1, value2);
}

但是myFcn() 看起来像什么?它应该只是中继到 sprintf 但在 sprintf 中支持可变参数所需的语法让我感到困惑。是否可以传递可变数量的参数?我在示例中显示了两个值,但不同的子项将具有不同数量的值,因此我必须保留 sprintf 的可变参数功能。请注意,性能在这里很重要,所以我不能有不必要的字符串副本。

如果在这种情况下有更好/更有效的 sprintf 替代方案,我可以考虑。谢谢。

【问题讨论】:

  • this 是您要找的吗?
  • @chris 这个答案来自 2009 年。C++11 有更好的可能性。
  • 不要在底部使用vsprintf,而是使用C++ 可变参数模板函数。这是引入可变参数模板时最常提供的示例之一。
  • 由于函数是virtual,所以template将不适用(直接)。
  • @KonradRudolph,同意。那纯粹是为了如何传递可变参数。

标签: c++ inheritance virtual-functions


【解决方案1】:

Sutter and Alexandrescu's C++ Coding Standards,您可以找到以下项目:

  1. 考虑将虚拟功能设为非公共,将公共功能设为非虚拟。 68

我相信,即使您的问题并未具体涉及公共/私人方面,但其中的基本原理仍然存在。非virtual 方法应该说明它“在外部”做什么,而virtual 方法应该说明如何 它是做什么的。

因此,它们不需要具有相同的签名。其实这样怎么样:

class Parent
{
private:
    char outputBuffer[100];

protected:
    virtual void WriteOutputImp(char outputBuffer[100]) = 0;

public:
    void WriteOutput()
    {
        WriteOutputImp(outputBuffer);
   }
};


class Child : public Parent
{
private:
    double value1;
    double value2;

protected:
    virtual void WriteOutputImp(char outputBuffer[100])
    {
        sprintf(outputBuffer, "%.2f %.2f", value1, value2);
    }
};

编辑正如@5gon12eder 正确(恕我直言)提到的那样,当您使用它时,您可能会考虑将char ...[100] 更改为更安全的东西。这是完全正确的,但需要单独说明。

【讨论】:

  • 我认为这是一个很好的答案,但void WriteOutputImp(char outputBuffer[100]) 签名具有误导性。我宁愿让函数接受一个指针和一个长度,并在内部使用snprintf
  • @5gon12eder 谢谢,这听起来很合乎逻辑。既然这是一个正交点,那么为了避免混淆,我想我会在后面提到它。赞赏!
【解决方案2】:

您可以使用新的可变参数模板。即

#include <iostream>
#include <iomanip>

void write()
{  }

template<typename... Params>
void write(double toPrint, Params... args)
{
    std::string str = "%.2f";
    const int size = sizeof...(args);
    if(size > 0U)
        str += " ";
    sprintf(outputBuffer, str.c_str(), toPrint);

    write(args...);
}

您可以通过单独的 WriteOutput 方法调用它,例如 write(5.1, 1.23, mydoublevalue, 9.993)。 但是,您可能希望尽量减少 sprintf 调用,从而需要另一种方法:

template<typename... Params>
std::string construct()
{ return std::string(); }

template<typename... Params>
std::string construct(double toPrint, Params... args)
{
    std::string str = "%.2f";
    const int size = sizeof...(args);
    if(size > 0U)
        str += " ";
    str += construct(args...);

    return str;
}

template<typename... Params>
void write(Params... args)
{
    sprintf(outputBuffer, (construct(args...)).c_str(), args...);
}

呼叫是相同的,但您只使用一个sprintf 呼叫。看到它工作online

【讨论】:

    猜你喜欢
    • 2016-03-04
    • 2020-10-26
    • 1970-01-01
    • 2013-02-26
    • 2012-12-19
    • 1970-01-01
    • 2019-05-14
    • 1970-01-01
    相关资源
    最近更新 更多