[英]Is correct to use `dynamic_cast` to infer the type of an argument of a member function defined on the base class and implemented on a derived class?
我剛開始使用C ++。 我正在嘗試為矢量操作的子集設計一個類(接口)。 此抽象基類定義為:
//file: vect.hh
#ifndef _vect_hh
#define _vect_hh
class vect
{
public:
virtual double norm() const = 0;
virtual void add(const double scaleThis, const double scaleOther,
const vect& other) = 0;
virtual double dot(const vect& other) const = 0;
virtual vect* clone() const = 0;
virtual vect* copy(const vect& other) const = 0;
virtual ~vect()=default;
};
#endif
問題出現在具有const vect& other
參數的const vect& other
。 因為在派生類中我真正想要的是const vectDerived& other
作為參數。 為了舉例說明這個問題,我使用原始指針對前一個類進行了簡單的實現。 因為我還有其他一些問題,我將在這個問題的最后給出評論,我已經插入了該類的完整定義。 但請記住,最重要的功能是dot
和add
:
// file: vectDouble.hh
#ifndef _vectDouble_hh
#define _vectDouble_hh
#include <cmath>
#include <cstring>
#include "vect.hh"
class vectDouble: public vect
{
public:
explicit vectDouble(const int n): n{n}, elem{new double[n]}
{
std::memset(elem,'\0',sizeof(double)*n);
}
~vectDouble() override {delete[] elem;}
vectDouble(const vectDouble& other): n{other.n}, elem{new double[n]}
{
std::memcpy(elem, other.elem, n*sizeof(double));
}
vectDouble& operator=(const vectDouble& other)
{
if(&other != this){
delete[] elem; n = other.n;
elem = new double[n];
std::memcpy(elem, other.elem, sizeof(double)*n);
}
return *this;
}
vectDouble(vectDouble&& other): n{0}, elem{nullptr}
{
fillClass(other, *this);
}
vectDouble &operator=(vectDouble&& other)
{
if(&other != this){
delete[] elem;
fillClass(other, *this);
other.elem = nullptr; other.n = 0;
}
return *this;
}
double norm() const override
{
double norm = 0.0;
for(int i=0;i<n;i++){norm += elem[i]*elem[i];}
return std::sqrt(norm);
}
double dot(const vect& other) const override
{
const vectDouble &v = dynamic_cast<const vectDouble&>(other);
double dot = 0.0;
for(int i=0;i<n;i++){dot += elem[i]*v.elem[i];}
return dot;
}
void add (const double scaleThis, const double scaleOther,
const vect& other) override
{
const vectDouble &v = dynamic_cast<const vectDouble&>(other);
for(int i=0;i<n;i++){
elem[i] = scaleThis*elem[i] + scaleOther*v.elem[i];
}
}
double& operator[](const int i){return elem[i];}
const double& operator[](const int i) const {return elem[i];}
int size() const{return n;}
vectDouble* clone() const override
{
return new vectDouble(*this);
}
vectDouble* copy(const vect& other) const override
{
const vectDouble &v = dynamic_cast<const vectDouble&>(other);
auto *t = new vectDouble(*this);
t->n = v.n;
std::memcpy(t->elem, v.elem, t->n*sizeof(double));
return t;
}
private:
void fillClass(const vectDouble& in, vectDouble& out)
{
out.n = in.n; out.elem = in.elem;
}
int n;
double *elem;
};
#endif
在這兩個函數中我使用了const vectDouble &v = dynamic_cast<const vectDouble&>(other);
將基類引用轉換為具有派生類類型的引用。 這是dynamic_cast
的有效用例。 如果沒有,實現這一結果的正確方法是什么?
我已經說過我遇到了其他問題(抱歉偏離了主要問題)。 作為使用抽象類和先前實現的例子,我做了這個簡單而有點人為的主程序:
// file main.cc
#include <iostream>
#include <memory>
#include "vectDouble.hh"
double lsfit(const vect& dobs, const vect& dcalc)
{
std::unique_ptr<vect> tmp(dcalc.copy(dcalc));
return (dobs.dot(dcalc))/(dcalc.dot(*tmp));
}
void testFit()
{
vectDouble d{10};
vectDouble x{10};
for(int i=0;i<x.size();i++){
d[i] = static_cast<double>(3*i);
x[i] = static_cast<double>(i);
}
std::cout<<"alpha="<<lsfit(d, x)<<std::endl;
}
int main()
{
testFit();
return 0;
}
該程序說明了為所描述的接口設想的一個用例。 但是,如果不使用std::unique_ptr
,則會發生內存泄漏(使用選項-fsanitize=leak
g ++編譯器中的泄漏標識)。 如果不是使用unique_ptr我想手動管理內存(作為好奇心),清除此結果的正確方法是什么? 可以直接從復制函數返回std::unique_ptr
。 當我嘗試這樣做時,我收到了與錯誤的協變返回類型相關的錯誤消息。
備注:1)此接口的目的是抽象用於表示數組的存儲方案,例如文件而不是內存表示。 2)我知道所提供的復制功能更類似於創建/克隆加復制功能。 3)如果將來我想在基類和派生類中使用模板,那么所呈現的結構是否足夠? 例如template<float> class vect{...}
和template <float> class vectDerived{...}
?
根據@hayt的建議,我已經更改了vect.hh
和vectDouble.hh
的定義,以使用所描述的CRTP模式。 在這些更改后,我還將函數lsftit的定義更改為:
template <class Derived> double lsfit2(const Derived& dobs, const Derived& dcalc)
{
std::unique_ptr<Derived> tmp = dcalc.clone();
Derived *t = tmp.get();
t->copy(dcalc);
return (dobs.dot(dcalc))/(dcalc.dot(*t));
}
這是使用此模式時定義此功能的正確方法嗎?
謝謝。
您應該檢查是否確實需要繼承,並且可能使用模板參數切換到一個通用向量類(從您只有“double”作為特定的東西)
另一種方法是將CRTP與聲明接口結合使用。 (我還在這個apporach中添加了unique_ptr)
template <class Derived>
class vect
{
public:
virtual double norm() const = 0;
virtual void add(const double scaleThis, const double scaleOther,
const Derived& other) = 0;
virtual double dot(const Derived& other) const = 0;
virtual std::unique_ptr<Derived> clone() const = 0;
virtual std::unique_ptr<Derived> copy(const vect& other) const = 0;
virtual ~vect()=default;
};
這樣你就擁有了相同的“接口”,但卻有一個不同的“基類”,你的函數中也有派生類。 因此,您不必擔心通過接口將“不兼容”向量相互分配(請參閱dynamic_cast)。
此外,您可以稍后派生該類以獲得更多規范。
以下是您使用此方法看起來的類:
class vectDouble: public vect<vectDouble>
{
public:
//...
//NOTE: here vecDouble is a parameter. no need for dynamic casts
double dot(const vectDouble& other) const override
{
double dot = 0.0;
for(int i=0;i<n;i++){dot += elem[i]*other.elem[i];}
return dot;
}
void add (const double scaleThis, const double scaleOther,
const vectDouble& other) override
{
for(int i=0;i<n;i++){
elem[i] = scaleThis*elem[i] + scaleOther*other.elem[i];
}
}
//also directly a unique pointer
std::unique_ptr<vectDouble> clone() const override
{
return std::make_unique<vectDouble>(*this);
}
//...
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.