用于将多态基类的子类数组传递到构造函数中的最简单语法

问题描述

这是工作代码,我想简化其调用。让我们从通话开始:

Config c2( {
           new ConfigPairInt(  "one",1   ),new ConfigPairDouble( "two",2.0 ),new ConfigPairstring( "three","3" )
} );

我要摆脱的第一件事是new。 (如果我这样做new,我想那是内存泄漏了吗?)但是,当我使Config的构造函数采用vector<ConfigPair&>删除这些new时,我得到了页面对我来说没有意义的错误

我想摆脱的第二件事是类型类名称。如果改用花括号初始化器,则编译器确实知道braced-initializer列表为ConfigPair*,因此它看到的所有内容都应为ConfigPair的初始化器或其子类之一。但是,编译器实际上在搜索子类构造函数吗?显然不是。 (我确实有一个想法,就是让ConfigPair完成其所有子类的工作,并为其提供三个不同的构造函数。它不再是多态的。并且不再是多态的,那么我也可以摆脱它。 new,因为向量可能是此类ConfigPair的类,而不是ConfigPair*的类,因为多态性需要它,但是多态性似乎完全适合该问题,因此我不愿放弃它。)

我想摆脱的第三件事是vector初始化程序周围的花括号。我想我可以使用参数包来做到这一点,但是我无法获得子类参数的结果列表来初始化向量。

class ConfigPair {
public:
  ConfigPair( const char* pszName_in ) :
    pszName( newstring( pszName_in ) )
  {
  };

  virtual ~ConfigPair() {
    free( (char*) pszName );
  };

  const char* pszName;
};



class Config {
public:
 
  Config( std::vector<ConfigPair*> apcpair )
  {
      for( ConfigPair* pcpair: apcpair )
          // do something
  }
};



class ConfigPairInt : public ConfigPair {
public:
  ConfigPairInt( const char* pszName_in,int iValue_in ) :
    ConfigPair( pszName_in ),iValue( iValue_in )
  {
  };

  int         iValue;
};



class ConfigPairDouble : public ConfigPair {
public:
  ConfigPairDouble( const char* pszName_in,double dValue_in ) :
    ConfigPair( pszName_in ),dValue( dValue_in )
  {
  };
  double      dValue;
};

  

class ConfigPairstring : public ConfigPair {
public:
  ConfigPairstring( const char* pszName_in,const char* pszValue_in ) :
    ConfigPair( pszName_in ),pszValue( newstring( pszValue_in ) )
  {
  };

  virtual ~ConfigPairstring() {
    free( (char*) pszValue );
  };

  const char* pszValue;
};

解决方法

这是我在您的情况下所做的工作的原始草案(据描述),注释中的解释:

using ValueType = std::variant<std::string,int,double>; // To get rid of lengthy
                                                        // type names,and 
                                                        // have them
                                                        // change- and  maintainable 
                                                        // at a single point in your 
                                                        // source code
class ConfigPair {
public:
  ConfigPair( const std::string& name_in,ValueType value ) : 
                              // ^^^^^^^ Ditch "Hungarian notation" for heaven's sake!
    name( name_in ) {}

  virtual ~ConfigPair() {}; // Destructor doesn't need to do anything
                            // std::string manages memory already

  std::string name; // Is compatible with const char* for initialization
  ValueType  value;
};

using PConfigPair = std::shared_ptr<ConfigPair>;
// or in case that Config should take full ownership of ConfigPair's
// using PConfigPair = std::shared_ptr<ConfigPair>;

class Config {
public:
  Config( std::vector<PConfigPair> apcpair )
  {
      for( PConfigPair pcpair: apcpair )
          // do something
  }
// Maybe store the vector here as class member,if Config should take full
// ownership of those ConfigPair's
// std::vector<PConfigPair> config_pairs;
//
// In any case you don't need to care about manual memory management
};

Config c2( { std::make_shared<ConfigPair>("one",1),// std::make_unique alternatively
             std::make_shared<ConfigPair>("two",2.0),std::make_shared<ConfigPair>("three","3") } );

文档:

,

通过为new构造函数使用variadic template,可以很容易地摆脱对vectorConfig的使用,例如:

class Config
{
public:
    template<typename... Ts>
    Config(const Ts&... args)
    {
        const ConfigPair* arr[] = {&args...};
        // use arr as needed...
        for( const ConfigPair *arg: arr ) {
            // do something with arg...
        }
    }
};

int main()
{
    Config c2(
        ConfigPairInt("one",ConfigPairDouble("two",ConfigPairString("three","3")
    );
    // use c2 as needed...
    return 0;
}

Live Demo

如果要使用多态性,则根本无法摆脱派生的类型名称,因为编译器需要知道要创建的类型。单独使用大括号初始化器不会告诉编译器WHICH类型:

Config c2(
    {"one",1},// error: WHICH type?
    {"two",2.0},// error: WHICH type?
    {"three","3"} // error: WHICH type?
);

如果您想要这种语法,则完全摆脱多态性,只需ConfigPair本身就可以通过std::variantstd::any或等效方法处理任何值类型。

,

AS Remy和Panta在其他答案中指出:如果您想使用多态性,就无法摆脱派生类型名称,因为编译器需要知道要创建哪种类型。由于这是呼叫者最打字的方式,因此消除它是优先事项。因此排除了多态性。

取而代之的是,原来是用于不同种类对的虚拟基础类,现在变成了“瑞士军刀”,能够完成其子类的工作。它具有各种构造函数,并且增加了枚举的开销,该枚举要知道调用哪个构造函数。
public:
  ConfigPair( const char* pszName_in,int iValue_in ) {
    cfgpair.type    = TypeInt64;
    cfgpair.pszName = newstring( pszName_in );
    cfgpair.u.i     = iValue_in;
  }

  ConfigPair( const char* pszName_in,double dValue_in ) {
    cfgpair.type    = TypeDouble;
    cfgpair.pszName = newstring( pszName_in );
    cfgpair.u.d     = dValue_in;
  }

  ConfigPair( const char* pszName_in,const char* pszValue_in ) {
    cfgpair.type     = TypeSz;
    cfgpair.pszName  = newstring( pszName_in );
    cfgpair.pszValue = newstring( pszValue_in );
  }

鉴于ConfigPair现在是传递给Config构造函数的唯一类,这意味着编译器对构造函数的类型要求的了解使其可以在调用时推断出对象类型。因此,无需使用类名。

此外,由于数组内容现在是同质的,我们可以将ConfigPair本身放入数组中,而不是(最初需要)仅将 pointers 存储到{ {1}}子类对象。因此,是新的(或替代性的和非内存泄漏的,但更冗长的ConfigPair表示法)。

shared_pointer<ConfigPairXXX>()

对于呼叫者:

  Config( const char* pszName_in,// name only used for debugging messages
          std::vector<ConfigPair> acpair ) :
      Config( pszName_in )
  {
      for ( const ConfigPair cpair: acpair )
          cpair.Set( this );
  }

最后,将大括号放在括号初始化列表的外面。我探索了用参数包替换向量的方法,但是后来我不知道如何使编译器再知道类型了,因此再次需要该类型。要继续进行此操作,就调用方的简单性而言,将向前退了十步,因此我毕竟坚持使用braced-initializer-list。 (如果还有其他替代方法可以在不需要类名的情况下摆脱这些外花括号,请告诉我。)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...