首页 > 解决方案 > Only know data type at run time. How to hide data details to other classes which use them

问题描述

Basically I have to populate objects based on an input text file with the following syntax:

float 4.55 24 2.1
int 4 6 9 0
float 5.1 6 6
//char 255 3 5

And then I need to make some sort of operations on them (for example a simple adition) there is no way to know beforehand which types of data there will be.

I can't store them all internally as double variables because space optimitation is important as well as not loosing precision.

I thought of doing something like:

class Base {
public:
    virtual size_t size() = 0;
    virtual void addValue () = 0;
    virtual void getValue (int index) = 0;
};

class BaseFloat : public Base{
public:
    vector<float> data;
    void addValue (float d);
    float getValue (int index);
    size_t size();
}

class BaseInt : public Base{
public:
    vector<int> data;
    void addValue (int d);
    Int getValue (int index);
    size_t size();
}
/* other classes for each data type here*/

This doesn't work as each function has diferent return types or parameter needs;

Then I have one class which creates the correct object for each line.

The problem comes when I need to have other class which should work with any type of Base. I was thinking something like:

class OtherClass {
public:
    void addValue(Base*, double);
}

OtherClass my_class; //whatever
Base* a = new BaseFloat ();
Base* b = new BaseInt();

my_class.addData(a, 5.56); //Uses BaseFloat::addValue
my_class.addData(b, 6);  //Uses BaseInt::addValue
my_class.addData(b, 6.55); //Uses BaseInt::addValue

I was hoping I could do this without adding some sort of long if-else clause like:

void OtherClass::addDataHelper (Base* pointer)
if (subclass(pointer) == float)
    //Do BaseFloat* a = pointer;
    //Do a->addValue
else if ...

Any ideas?

标签: c++c++11runtimestd

解决方案


创建面向对象设计时,您需要定义基类的接口,因此所有派生类都可以在不更改签名的情况下覆盖虚函数。例如,对于读取数据,它可以这样实现:

class BaseData {
public:
    virtual bool addValue( std::istream &in ) = 0;
};

class DataFloat : public BaseData {
    virtual bool addValue( std::istream &in ) override
    {
         float v = 0;
         if( not in >> v ) return false;
         data_.push_back( v );
         return true;
    }
private:
    std::vector<float> data_;
};

class Factory {
public:
    std::unique_ptr<BaseData> create( const std::string &type );
};

std::vector<std::unique_ptr<BaseData>> parsing( std::istream &in, Factory &f )
{
    std::vector<std::unique_ptr<BaseData>> v;
    std::string str;
    while( std::getline( in, str ) ) {
        std::istringstream line( str );
        std::string type;
        if( not in >> type ) continue;
        auto pbase = f.create( type );
        while( pbase->addValue( in ) );
        v.push_back( std::move( pbase ) );
    }
    return v;
}

当然这不是完整的代码,但应该足以说明这个想法。因此,对于您的操作,您需要做同样的事情 - 找到在基类中定义虚函数的方法,这样就可以在每个派生的函数中重新实现它而无需更改签名。您对每个类的方法void addValue( int data )是否具有void addValue( float data )正确的 OO 设计,并且将导致级联或类似方法。ifdynamic_cast

另一种解决方案是包含数据std::vector<std::variant<int,float,char,double>>(列出所有必要的类型)并编写适当的访问者来进行数据插入和计算等。互联网上有很多关于如何正确执行此操作的材料,并且超出了此答案的范围他们在这里。


推荐阅读