在运行时从任何多个类中选择性地继承

use*_*630 11 c++ oop inheritance

我正在做科学计算,而且我是初学c ++.MyNLP是一个包含所有问题数据和方法的类.我正在使用第三方库进行数值优化.每个第三方都是一个解决我问题的特定算法.为了使用每个库,我的MyNLP类需要从第三方库继承相应的类.

例如,

Class MyNLP :public IPOPT
{
};
Run Code Online (Sandbox Code Playgroud)

使我能够使用IPOPT算法来解决我的问题.同样的,

class MyNLP: public SQP
{
};
Run Code Online (Sandbox Code Playgroud)

使我能够使用SQP算法.

但就我而言,只有在运行时,程序才会决定它应该继承哪个类.我必须继承第三方课程之一.任何人都可以在cpp中实现这一目标吗?

use*_*042 25

您无法在运行时选择继承,因为结果类型始终由编译器在编译时确定.

您可以做的是应用策略模式:

在此输入图像描述

我们的想法是有一个抽象类来表示在以下方面使用的算法MyNLP:

class Data;

class NLPAlgo {
public:
    virtual ~NLPAlgo() = default;
    virtual void Apply(Data&) = 0;
};
Run Code Online (Sandbox Code Playgroud)

并提供使用IPOPT和的具体类SQP:

class IPOPTAlgo : public NLPAlgo {
    IPOPT ipopt;
public:
    void Apply(Data& data) {
        // Use ipopt to realize the implementation
    }
}; 

class SQPAlgo : public NLPAlgo {
    SQP sqp;
public:
    void Apply(Data& data) {
        // Use sqp to realize the implementation
    }
}; 
Run Code Online (Sandbox Code Playgroud)

进一步将该抽象类作为参数 MyNLP

class MyNLP {
    std::unique_ptr<NLPAlgo> algo_;
public:
    MyNLP(std::unique_ptr<NLPAlgo> algo) : algo_(algo) {}
    void Apply(Data& data) {
        algo->Apply(data);
    }
};
Run Code Online (Sandbox Code Playgroud)

然后,您可以在运行时配置应使用哪种算法MyNLP:

// Note:
// That code could be factored out to an Abstract Factory:
// https://sourcemaking.com/design_patterns/abstract_factory
// That is figured out in more detail in this answer:
// https://stackoverflow.com/a/44985054/8242698
std::unique_ptr<NLPAlgo> algo;
if(condIPOPT) {
    algo = std::make_unique<IPOPTAlgo>();
}
else if(condSQP) {
    algo = std::make_unique<SQPAlgo>();
}

Data data;
MyNLP myNLP(algo);

myNLP.Apply(data);
Run Code Online (Sandbox Code Playgroud)


Fra*_*ler 7

有一点模板魔法(在编程中没有神奇的东西)我认为这可以帮助你以你要求的方式实现你的目标.还有许多其他很好的答案,如策略模式,工厂,调度等,但这是一个版本,使用模板和继承来自所述库,同时选择哪一个通过使用模板专业化在运行时实例化.

#include <iostream>

class A {
public:
    int a = 1;
    A() {}
};

class B {
public:
    float b = 2.0f;
    B() {}
};

class C {
public:
    char c = 'c';
    C() {}
};

template<class T>
class D : public T {
public:
    D() : T() {}
};


int main( int argc, char** argv ) {
    D<A> dA;
    D<B> dB;
    D<C> dC;

    std::cout << "D<A>::a = " << dA.a << "\n";
    std::cout << "D<B>::b = " << dB.b << "\n";
    std::cout << "D<C>::c = " << dC.c << "\n";

    std::cout << "Press any key and enter to quit." << std::endl;
    char c;
    std::cin >> c;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在这里我已经展示了3个不同的混凝土或完整类型的类A,B,和C能代表你会用它来进行评价或计算来解决你的问题你的3个不同的可能库.类D是表示您的MyNLP类的模板类型.现在您可以MyNLP<A> mynlpA使用第一个库,因为您的类现在将继承它,依此类推.

然而; 这是在编译时而不是运行时完成的,您必须使用特定类型实例化该类.您可以使用此模板并通过if语句或某些已定义函数中的switch语句使用用户输入进行设置,以选择在运行时创建和使用的类的哪个版本.另请注意,我基于继承类的基类专门设计了不同的类模板构造函数.运行此代码段以了解我如何能够在运行时根据用户输入class template D<T>继承A, B, or C.

#include <iostream>
#include <string>
#include <algorithm>

class A {
public:
    int a = 1;
    A() {}
};

class B {
public:
    float b = 2.0f;
    B() {}
};

class C {
public:
    char c = 'c';
    C() {}
};

template<class T>
class D : public T {
public:
    D() : T() {}
};

template<>
D<A>::D() {
    std::cout << "Initialize Library A\n";
}

template<>
D<B>::D(){
    std::cout << "Initialize Library B\n";
}

template<>
D<C>::D() {
    std::cout << "Initialize Library C\n";
}       

int main( int argc, char** argv ) {    
    std::string entry;
    std::cout << "Please choose which library to chose from: `A`, `B` or `C`\n";
    std::cout << "Or `Q` to quit\n";

    std::cin >> entry;

    std::transform(entry.begin(), entry.end(), entry.begin(), ::toupper);

    while ( true ) {

        if (entry == std::string("Q")) {
            break;
        }

        if (entry == std::string("A")) {
            D<A> dA;
            std::cout << "D<A>::a = " << dA.a << "\n";
        }

        if (entry == std::string("B")) {
            D<B> dB;
            std::cout << "D<B>::b = " << dB.b << "\n";

        }

        if (entry == std::string("C")) {
            D<C> dC;
            std::cout << "D<C>::c = " << dC.c << "\n";
        }

        entry.clear();
        std::cout << "Please choose which library to chose from: `A`, `B` or `C`\n";
        std::cout << "Or `Q` to quit\n";

        std::cin >> entry;

        std::transform(entry.begin(), entry.end(), entry.begin(), ::toupper);

    } 

    std::cout << "Press any key and enter to quit." << std::endl;
    char c;
    std::cin >> c;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是这个方法有一个警告:如果基类有private members/functions你可能需要直接调用或使用它们,你将无法访问它们,但好处是你可以访问任何public或者protected,但这是数据封装的想法.


Rog*_*Gee 5

C++类继承是一个编译时构造:它在运行时被修复.编译器必须在编译时将此信息提供给它,以确定如何分配对象并确保类型安全.

您应该考虑改为实施工厂.在此设计中,工厂对象或函数根据运行时可用的输入决定生成哪个实例.您应该创建一个公共基类,它提供所需功能的接口.这应该包含一个或多个用于动态调用正确实现的虚函数(这称为动态调度).

以下是基于您的问题的简单示例:

class nlp_interface
{
public:
    virtual ~nlp_interface() = default;

    // This pure-virtual function has no implementation, forcing the
    // class to be abstract and for derived classes to implement the
    // member function.
    virtual void do_numeric_optimization(/* ... */) = 0;
};

class MyNLP_IPOPT:
    public nlp_interface,
    public IPOPT
{
public:
    // Provide a specific implementation.
    virtual void do_numeric_optimization(/* ... */);
};

class MyNLP_SQP:
    public nlp_interface,
    public SQP
{
public:
    // Provide a specific implementation.
    virtual void do_numeric_optimization(/* ... */);
};
Run Code Online (Sandbox Code Playgroud)

这里我使用多重继承来为MyNLP_*类提供一个众所周知的接口.这是因为我不能假设第三方基类具有可以使用的公共虚拟接口.如果他们这样做,那么直接创建第三方类的实例.但是,您似乎暗示您必须出于某种原因对它们进行子类化.

这是工厂.

#include <memory>
#include <exception>

using nlp_pointer = std::unique_ptr<nlp_interface>;
nlp_pointer factory_function(const std::string& input)
{
    if (input == "IPOPT") {
        return nlp_pointer( new MyNLP_IPOPT );
    }
    if (input == "SQP") {
        return nlp_pointer( new MyNLP_SQP );
    }

    throw std::runtime_error("unrecognized algorithm kind");
}
Run Code Online (Sandbox Code Playgroud)

要使用工厂,请在返回的实例(包含在智能指针中)上调用factory_function()并调用do_numeric_optimization()成员函数nlp_interface.它将通过动态调度调用正确的版本.

  • 我只能添加以考虑策略模式以及https://en.wikipedia.org/wiki/Strategy_pattern (2认同)

Tob*_*zel 5

在我看来,这是XY问题的一个实例:您想要一种方法来切换基类,而您实际上想要一种方法来使用您的代码用于不需要适应的不同求解器.

我最近自己用IPOPT实现了NLP解算器,我建议采用另一种方法:

为什么不首先实现一个实现评估NLP的所有相关方法的基类:

  • 客观价值
  • 客观梯度
  • Jacobian的(in)等式约束
  • 拉格朗日
  • 拉格朗日的黑森州

由于您的求解器可能会使用等效格式的标量,向量以及希望稀疏矩阵(以坐标格式:行向量,列向量,值向量)和相同的浮点类型(双精度),然后您可以实现轻量级包装(基于在解决者的接口上)围绕这个基类,而不是试图使用某种奇怪的运行时多态.