发布
社区首页 >问答首页 >使用`dynamic_cast`来推断在基类上定义并在派生类上实现的成员函数的参数类型是否正确?

使用`dynamic_cast`来推断在基类上定义并在派生类上实现的成员函数的参数类型是否正确?
EN

Stack Overflow用户
提问于 2016-09-20 15:30:28
回答 1查看 97关注 0票数 1

我只是从C++开始。我正在尝试为向量运算的子集设计一个类(接口)。此抽象基类定义为:

代码语言:javascript
代码运行次数:0
复制
//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 vectDerived& other这样的参数。为了举例说明这个问题,我使用原始指针对上一个类做了一个简单的实现。因为我还有其他一些问题要在这个问题的末尾发表意见,所以我插入了这个类的完整定义。但请记住,最重要的函数是dotadd

代码语言:javascript
代码运行次数:0
复制
// 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的有效用例。如果不是,怎样才能达到这个效果呢?

我已经说过,我遇到了其他问题(很抱歉偏离了主要问题)。作为使用抽象类和前面实现的示例,我制作了这个简单但有点做作的主程序:

代码语言:javascript
代码运行次数:0
复制
    // 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,就会出现内存泄漏(使用g++compiler中的选项-fsanitize=leak进行识别)。如果我想手动管理内存(出于好奇心),而不是使用unique_ptr,那么清理此结果的正确方法是什么?可以直接从复制函数返回一个std::unique_ptr。当我尝试这样做时,我收到了与错误的协变返回类型相关的错误消息。

备注: 1)此接口的目的是抽象用于表示数组的存储方案,例如文件而不是内存中的表示。2)我意识到所呈现的复制功能更类似于创建/克隆加复制功能。3)如果将来我想在基类和派生类中都使用模板,那么所呈现的结构是足够的?例如template<float> class vect{...}template <float> class vectDerived{...}

遵循@hayt的建议,我更改了vect.hhvectDouble.hh的定义,以使用所描述的CRTP模式。在这些更改之后,我还将函数lsftit的定义更改为:

代码语言:javascript
代码运行次数:0
复制
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));
}

在使用此模式时,这是定义此函数的正确方式吗?

谢谢。

EN

回答 1

Stack Overflow用户

发布于 2016-09-20 16:01:51

你应该检查你是否真的需要继承,也许可以切换到一个带有模板参数的泛型向量类(从你只有"double“作为特定的东西)

另一种方法是将CRTP与声明接口结合使用。(我还在此参数中添加了unique_ptr )

代码语言:javascript
代码运行次数:0
复制
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)。

此外,您可以稍后为进一步的规范派生该类。

下面是使用这种方法创建类时的样子:

代码语言:javascript
代码运行次数:0
复制
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);
    }
    //...
};
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39588196

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档