解决C ++中的循环引用变量依赖关系

时间:2018-07-18 15:02:43

标签: c++ oop

假设我有以下代码来使用C ++构建数据流体系结构

class DataflowClass {
    public:
        DataflowClass(int & _a, int & b) : a(_a), b(b){}

        inline void calculate(){
            c = a + b;
            d = a - 2*b;
        }

        inline int & getC() { return c; }
        inline int & getD() { return d; }

    private:
        int c = 1;
        int d = 2;
        int & a;
        int & b;
};

void TestReferenceVariableDataflow(int count){

    int a;
    int b;

    DataflowClass c1(a, b);
    DataflowClass c2(c1.getC(), c1.getD());
    DataflowClass c3(c2.getC(), c2.getD());

    for(int i=0;i<count;i++){
        a = c3.getC();
        b = c3.getD();
        c1.calculate();
        c2.calculate();
        c3.calculate();
    }
}

DataflowClass是具有两个输入和两个输出的数据流块的主要构造。 DataflowClass的构造函数接受两个参考变量作为输入,并具有两个getter函数,这些函数将参考返回给它的两个输出成员, 可以看出,在TestReferenceVariableDataflow函数中,我构造了3个DataflowClass实例,在c1实例中构造了对TestReferenceVariableDataflow函数中定义的两个局部变量的引用,在c2中构造了两个引用,分别来自c1.getC()和c2.getC (),反之亦然。

在每次迭代中,将3类的计算方法称为定义顺序,这将导致3类内的数据流计算。

真正的问题是循环依赖性,如您所见,c1是通过引用TestReferenceVariableDataflow函数中定义的两个变量构造的,并且在每次迭代结束时,我必须从c3.getC()和c3复制值。将getD()放入这些变量。

由于DataflowClass在其构造函数中接受引用变量,因此我无法将c3的输出变量直接连接到c1,因为c3实例在c1的构造时不可用,因此我必须定义两个额外的变量将每次迭代中的值从最后一次复制到第一次。

如您所知,具有引用成员的类应在构造时初始化引用,此问题使该循环依赖成为问题。

这个循环依赖问题有解决方案吗?

修改 这种设计背后有一些简单的原因,

1-首先,这是设计,仅使用C ++通用语法,不使用任何其他种类的库,甚至不使用std :: *,只是出于性能考虑,认为其中有数十个这些块,并且它们相互连接依次地 避免使用库的另一个原因是该代码是实时软件的一部分,其行为比库更容易预测

2- DataflowClass的实例将永远不会被破坏,并且我们不能有悬空的指针

3-在时间紧迫且实时的软件中,由于不可预测性而以某种方式禁止了动态内存管理,并且该设计确实可以用于所有静态内存分配

4-指针的使用不被认为是一个很好的解决方案,因为使用指针无法实现静态编译时声明,编译时接线可防止未连接的输入网络 而且指针可能会为null或类似的东西

5-此设计不需要为每个变量更改信号,因为所有计算方法都将被串行调用,并且也可能与父类一起自动调用

6-块的输入可能来自不同的块(与其之前的块不同),所提供的示例只是一个简单的情况,即块之间相互均匀连接

7-简单的设计是simulink块的C ++实现,块的输出连接到其他块的输入,它们将随着时间的增加计算其输出

4 个答案:

答案 0 :(得分:2)

您可以使用指针代替引用:

class DataflowClass {
public:
    DataflowClass() = default;

    DataflowClass(int& pa, int& pb) : a(&pa), b(&pb){}
    void linkTo(int& pa, int& pb) { a = &pa; b = &pb; }

    void calculate(){
        assert(a != nullptr && b != nullptr);
        c = *a + *b;
        d = *a - 2 * *b;
    }

    int & getC() { return c; }
    int & getD() { return d; }

private:
    int c = 1;
    int d = 2;
    const int* a = nullptr;
    const int* b = nullptr;

};

void TestReferenceVariableDataflow(int count)
{
    DataflowClass c1;
    DataflowClass c2(c1.getC(), c1.getD());
    DataflowClass c3(c2.getC(), c2.getD());
    c1.linkTo(c3.getC(), c3.getD());

    for(int i=0;i<count;i++){
        c1.calculate();
        c2.calculate();
        c3.calculate();
    }
}

但是指针可能为空:-/

具有以下内容似乎更简单:

std::pair<int, int> calculate(int a, int b) { return { a + b, a - 2 * b} ; }

void TestReferenceVariableDataflow(int count)
{
    int a = 1;
    int b = 2;

    for(int i=0;i<count;i++){
        std::tie(a, b)  = calculate(a, b);
        std::tie(a, b)  = calculate(a, b);
        std::tie(a, b)  = calculate(a, b);
    }
}

答案 1 :(得分:1)

我唯一可以看到仅使用引用的方法就是通过完全分离存储。

class DataFlowClass {
    const int& a;
    const int& b;
    int& c;
    int& d;

public:
    DataFlowClass(const int& a, const int& b, int& c, int& d) : a{a}, b{b}, c{c}, d{d} {
        c = 1;
        d = 2;
    }

    void calculate() {
        c = a + b;
        d = a - 2*b;
    }
};

void TestReferenceVariableDataflow(int count){
    int storage[6]; // could use 6 int variables instead

    DataflowClass c1(storage[0], storage[1], storage[2], storage[3]);
    DataflowClass c2(storage[2], storage[3], storage[4], storage[5]);
    DataflowClass c3(storage[4], storage[5], storage[0], storage[1]);

    for(int i=0;i<count;i++){
        c1.calculate();
        c2.calculate();
        c3.calculate();
    }
}

但是,调用者有责任确保正确设置所有引用,这可能会变得很复杂。

  

由于您提到这是针对嵌入式环境的,因此请注意,此解决方案可能占用Jarod42's answer中基于指针的解决方案所需的内存量的1.5倍。

答案 2 :(得分:0)

您的设计过于复杂,没有太多理由:

class DataflowClass {
    public:
        void calculate(const DataflowClass &from ){
            c = from.getC() + from.getD();
            d = from.getC() - 2*from.getD();
        }

        int getC() const { return c; }
        int getD() const { return d; }

    private:
        int c = 1;
        int d = 2;
};

void TestReferenceVariableDataflow(int count){
    std::array<DataflowClass,3> c;

    for(int i=0;i<count;i++){
        for( size_t i = 0; i < c.size(); ++i ) {
            auto prev = ( i == 0 ? c.size() : i ) - 1;
            c[i].calculate( c[prev] ); 
        }
    }
}

这是更通用,更安全的方法。

答案 3 :(得分:0)

最后使用std :: reference_wrapper找到了最佳解决方案

class DataflowClass {
    public:
        DataflowClass(){}

        inline void setInputs(int & _a, int & _b){
            this->a = _a ;
            this->b = _b;
        }

        inline void calculate(){
            c = a + b;
            d = a - 2*b;
        }

        inline int & getC() { return c; }
        inline int & getD() { return d; }

    private:
        int c = 1;
        int d = 2;
        int a_initial = 0;
        int b_initial = 0;
        std::reference_wrapper<int> a = a_initial;
        std::reference_wrapper<int> b = b_initial;
};

void TestReferenceVariableDataflow(int count){

    DataflowClass c1, c2, c3;
    c2.setInputs(c1.getC(), c1.getD());
    c3.setInputs(c2.getC(), c2.getD());
    c1.setInputs(c3.getC(), c3.getD());

    for(int i=0;i<count;i++){
        c1.calculate();
        c2.calculate();
        c3.calculate();
    }
}