gpt4 book ai didi

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

转载 作者:太空宇宙 更新时间:2023-11-04 13:11:35 25 4
gpt4 key购买 nike

我刚开始接触 C++。我正在尝试为 vector 操作的子集设计一个类(接口(interface))。此抽象基类定义为:

//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 :

// 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存在内存泄漏(使用 g++ 编译器的选项 -fsanitize=leak 识别)。如果我不想使用 unique_ptr 而想手动管理内存(出于好奇),清理此结果的正确方法是什么?可以直接返回 std::unique_ptr从复制功能。当我尝试这样做时,我收到了与错误的协变返回类型相关的错误消息。

备注:1) 此接口(interface)的目的是抽象用于表示数组的存储方案,例如文件而不是内存表示。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));
}

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

谢谢。

最佳答案

你应该检查你是否真的需要继承并且可能切换到一个带有模板参数的通用 vector 类(因为你只有“double”作为特定的东西)

另一种方法是结合使用 CRTP 和声明接口(interface)。 (我还在这个方法中添加了 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;
};

这样你就有了相同的“接口(interface)”但有不同的“基类”并且你在这里的函数中也有派生类。因此,您不必担心通过接口(interface)将“不兼容” vector 分配给彼此(参见 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);
}
//...
};

关于c++ - 使用 `dynamic_cast` 来推断在基类上定义并在派生类上实现的成员函数的参数类型是否正确?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39588196/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com