Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/160.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 当依赖项和依赖项都是多态的时,在哪个继承级别存储依赖项指针?_C++_Dependency Injection_Dependencies_Polymorphism_Smart Pointers - Fatal编程技术网

C++ 当依赖项和依赖项都是多态的时,在哪个继承级别存储依赖项指针?

C++ 当依赖项和依赖项都是多态的时,在哪个继承级别存储依赖项指针?,c++,dependency-injection,dependencies,polymorphism,smart-pointers,C++,Dependency Injection,Dependencies,Polymorphism,Smart Pointers,我目前正在处理一种情况,即多态对象具有注入依赖项,该依赖项也是多态的。我的问题是关于第一个类家族拥有家族共同行为的最佳方式,需要调用首先在第二个家族的顶级基类中定义的虚拟方法。具体地说,是关于将所属的智能指针存储到依赖项的位置—在基类中、在派生类中或在这两个位置。(这个问题是关于在任务中使用智能指针,但在使用DI的引用或原始指针时也存在类似的问题。) 我将使用一些示例基类Worker和Job来说明。每个工人都有一份工作,每个人都有一份工作。DerivedWorkers可能要求用户注入特定的Der

我目前正在处理一种情况,即多态对象具有注入依赖项,该依赖项也是多态的。我的问题是关于第一个类家族拥有家族共同行为的最佳方式,需要调用首先在第二个家族的顶级基类中定义的虚拟方法。具体地说,是关于将所属的智能指针存储到依赖项的位置—在基类中、在派生类中或在这两个位置。(这个问题是关于在任务中使用智能指针,但在使用DI的引用或原始指针时也存在类似的问题。)

我将使用一些示例基类Worker和Job来说明。每个工人都有一份工作,每个人都有一份工作。DerivedWorkers可能要求用户注入特定的DerivedJob,并调用特定于该DerivedJob的方法。每个Worker都必须具有公共方法get_location(),此函数的逻辑对于所有Worker都是相同的,并且它需要调用在Job中定义的虚拟方法get_hours(),并由其子级进行重写。以下是我提出的三种策略,“指针在基中并派生”,“指针仅在派生中”和“指针仅在基中”:

策略1:指针位于基和派生

class Worker
{
public:
    Worker(std::shared_ptr<Job> job) : my_job(job) {}
    virtual ~Worker();
    Location_t get_location()
    {
        return some_logic(my_job->get_hours());
    }
private:
    std::shared_ptr<Job> my_job; //cannot be unique_ptr
};

class DerivedWorker : public Worker
{
public:
    DerivedWorker(std::shared_ptr<DerivedJob> derivedJob) : Worker(derivedJob), my_derived_job(derivedJob) {}
    void derived_specific_duty()
    {
        my_derived_job->derived_specific_method();
    }
private:
    std::shared_ptr<DerivedJob> my_derived_job;
};
类工作者
{
公众:
Worker(std::shared_ptr job):我的工作(job){
虚拟工作者();
位置\u无法获取\u位置()
{
返回一些逻辑(my_job->get_hours());
}
私人:
std::shared_ptr my_job;//不能是唯一的\u ptr
};
工人阶级:公共工作者
{
公众:
DerivedWorker(std::shared_ptr derivedJob):Worker(derivedJob),my_派生的_job(derivedJob){}
无效派生的特定职责()
{
my_派生的_作业->派生的_特定的_方法();
}
私人:
std::共享\u ptr我的\u派生\u作业;
};
策略2:仅在派生中使用指针

class Worker //abstract
{
public:
    virtual ~Worker();
    virtual Location_t get_location() = 0;
};

class DerivedWorker : public Worker
{
public:
    DerivedWorker(std::unique_ptr<DerivedJob> derivedJob) : my_derived_job(derivedJob) {}
    virtual Location_t get_location()
    {
        return some_logic(my_derived_job->get_hours());
    }
    void derived_specific_duty()
    {
        my_derived_job->derived_specific_method();
    }
private:
    std::unique_ptr<DerivedJob> my_derived_job;
};
类工作者//抽象 { 公众: 虚拟工作者(); 虚拟位置\u t获取\u位置()=0; }; 工人阶级:公共工作者 { 公众: DerivedWorker(std::unique_ptr derivedJob):我的派生作业(derivedJob){} 虚拟位置\u无法获取\u位置() { 返回一些_逻辑(我的_派生的_作业->获取_小时(); } 无效派生的特定职责() { my_派生的_作业->派生的_特定的_方法(); } 私人: std::唯一的\u ptr我的\u派生的\u作业; }; 策略3:指针只在底部

class Worker
{
public:
    Worker(std::unique_ptr<Job> job) : my_job(job) {}
    virtual ~Worker();
    Location_t get_location()
    {
        return some_logic(my_job->get_hours());
    }
protected:
    std::unique_ptr<Job> my_job;
};

class DerivedWorker : public Worker
{
public:
    DerivedWorker(std::unique_ptr<DerivedJob> derivedJob) : Worker(derivedJob) {}
    void derived_specific_duty()
    {
        dynamic_cast<DerivedJob*>(my_job.get())->derived_specific_method();
    }
};
类工作者
{
公众:
工作者(std::unique_ptr job):我的工作(job){
虚拟工作者();
位置\u无法获取\u位置()
{
返回一些逻辑(my_job->get_hours());
}
受保护的:
std::独特的我的工作;
};
工人阶级:公共工作者
{
公众:
DerivedWorker(std::unique_ptr derivedJob):Worker(derivedJob){}
无效派生的特定职责()
{
动态_cast(my_job.get())->派生的_特定的_方法();
}
};
每种方法都有缺点,我试图找出我是否缺少第四种方法,是否有一种惯用的或“最佳”的方法,或者是否缺少一些使这种依赖模式过时的重构技巧

对于1,“基指针和派生指针”,缺点是即使每个作业仅由一个工人拥有,也不能使用unique_ptr,因为从技术上讲,每个工人可以拥有指向同一作业的多个智能指针。如果工作线程频繁移动,或者工作线程之间交换作业,这可能是一个问题,因为移动共享线程会导致缓存内聚速度减慢。这是我目前倾向的策略

对于2,“仅派生指针”,缺点是大量代码重复。get_location()必须是虚拟的,尽管所有工作人员的代码几乎完全相同。此外,现在工人可能必须抽象。(在这个特定示例中,您可以通过为Location_t设置空值来避免这种情况,但这在该问题的实际应用中并不总是可行的。)


对于3,“指针只在底部”,缺点是必须使用dynamic_cast,这是一种巨大的代码气味,这是有原因的。巨大的运行时成本,必须添加对失败的cast案例的检查,等等。

我会选择2的变体:

而不是
虚拟位置\u t get\u Location()=0最好拥有
虚拟作业&get_Job()=0,所以您可以在派生类中使用协变返回类型,
get\u location()
的实现不会重复

class Worker // abstract
{
public:
    virtual ~Worker() = default;
    virtual Job& get_job() = 0;

    Location_t get_location() { return some_logic(get_job().get_hours()); }
};

class DerivedWorker : public Worker
{
public:
    explicit DerivedWorker(std::unique_ptr<DerivedJob> derivedJob) : my_job(std::move(derivedJob)) {}

    DerivedJob& get_job() override { return *my_job;}

    void derived_specific_duty() { my_job->derived_specific_method(); }
private:
    std::unique_ptr<DerivedJob> my_job;
};
类工作者//抽象 { 公众: virtual~Worker()=默认值; 虚拟作业&get_Job()=0; Location_t get_Location(){返回一些逻辑(get_job().get_hours());} }; 工人阶级:公共工作者 { 公众: 显式DerivedWorker(std::unique_ptr derivedJob):我的作业(std::move(derivedJob)){} DerivedJob&get_job()重写{return*my_job;} void派生的特定的职责(){my_job->派生的特定的方法();} 私人: std::独特的我的工作; };
方法1-问题是重复数据,这既是开销又是bug的来源

方法3-问题是动态转换-这是一个非常缓慢的转换。你本可以只使用静态施法,但持续施法会很麻烦。由于基类几乎没有实现,因此不值得在其中存储类型,因为它仅限制其使用

方法2-是您提供的3种方法中唯一合理的方法

除此之外,我还质疑这些虚拟类的用途——它们是否有用途?仅仅拥有一个公共函数并不足以成为创建共享基类的理由。实际上,它需要帮助。不要仅仅因为他们说“面向对象很好”而创建基类


此外,我也会考虑模板方法。在对象众多且需要快速处理的情况下,面向对象的方法往往比不上基于模板的方法。

是否有一些特定的环境需要如此困难?“每个工人都有一个。”
class Worker // abstract
{
public:
    virtual ~Worker() = default;
    virtual Job& get_job() = 0;

    Location_t get_location() { return some_logic(get_job().get_hours()); }
};

class DerivedWorker : public Worker
{
public:
    explicit DerivedWorker(std::unique_ptr<DerivedJob> derivedJob) : my_job(std::move(derivedJob)) {}

    DerivedJob& get_job() override { return *my_job;}

    void derived_specific_duty() { my_job->derived_specific_method(); }
private:
    std::unique_ptr<DerivedJob> my_job;
};