C++ 当依赖项和依赖项都是多态的时,在哪个继承级别存储依赖项指针?
我目前正在处理一种情况,即多态对象具有注入依赖项,该依赖项也是多态的。我的问题是关于第一个类家族拥有家族共同行为的最佳方式,需要调用首先在第二个家族的顶级基类中定义的虚拟方法。具体地说,是关于将所属的智能指针存储到依赖项的位置—在基类中、在派生类中或在这两个位置。(这个问题是关于在任务中使用智能指针,但在使用DI的引用或原始指针时也存在类似的问题。) 我将使用一些示例基类Worker和Job来说明。每个工人都有一份工作,每个人都有一份工作。DerivedWorkers可能要求用户注入特定的DerivedJob,并调用特定于该DerivedJob的方法。每个Worker都必须具有公共方法get_location(),此函数的逻辑对于所有Worker都是相同的,并且它需要调用在Job中定义的虚拟方法get_hours(),并由其子级进行重写。以下是我提出的三种策略,“指针在基中并派生”,“指针仅在派生中”和“指针仅在基中”: 策略1:指针位于基和派生C++ 当依赖项和依赖项都是多态的时,在哪个继承级别存储依赖项指针?,c++,dependency-injection,dependencies,polymorphism,smart-pointers,C++,Dependency Injection,Dependencies,Polymorphism,Smart Pointers,我目前正在处理一种情况,即多态对象具有注入依赖项,该依赖项也是多态的。我的问题是关于第一个类家族拥有家族共同行为的最佳方式,需要调用首先在第二个家族的顶级基类中定义的虚拟方法。具体地说,是关于将所属的智能指针存储到依赖项的位置—在基类中、在派生类中或在这两个位置。(这个问题是关于在任务中使用智能指针,但在使用DI的引用或原始指针时也存在类似的问题。) 我将使用一些示例基类Worker和Job来说明。每个工人都有一份工作,每个人都有一份工作。DerivedWorkers可能要求用户注入特定的Der
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;
};