簡體   English   中英

使用`dynamic_cast`來推斷在基類上定義並在派生類上實現的成員函數的參數類型是正確的嗎?

[英]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作為參數。 為了舉例說明這個問題,我使用原始指針對前一個類進行了簡單的實現。 因為我還有其他一些問題,我將在這個問題的最后給出評論,我已經插入了該類的完整定義。 但請記住,最重要的功能是dotadd

// 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.hhvectDouble.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM