使用' 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 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, ' ', 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
?当我尝试这样做时,我收到了与错误的协变返回类型相关的错误消息。
备注:
- 这个接口的目的是抽象用于表示数组的存储方案,例如一个文件而不是内存中的表示。
- 我知道所呈现的复制功能更类似于创建/克隆加上复制功能。
- 如果将来我想在基类和派生类中都使用模板,那么结构是否足够?例如
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与声明接口结合使用。(我还在这种方法中添加了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);
}
//...
};
- 如何使用指针传递给函数的数组中对象的函数成员
- c++构造函数成员初始化:传递参数
- 创建 std::函数,它返回具有函数成员值的变量.分段错误
- 如何在C++通过公共函数访问私有函数成员?
- 解释了构造函数成员初始化列表
- 调用std::函数成员时内存损坏
- 是否可以为模板类的模板函数成员设置别名?
- 捕获 lambda 函数C++成员变量
- 构造函数成员初始值设定项跨成员列出,安全吗?
- 获取与在模板参数中传递的函数成员类型相同的类
- 如何从公共函数成员访问地图私有成员
- C 构造函数成员分配优化
- 使用命名空间进行函数成员定义
- 函数成员作为 CUDA 内核的参数
- 模板基类函数成员的别名
- 函数成员中用于void和继承的enable_if
- 头文件中是否定义了一个很长的Class函数成员
- 类内/构造函数成员初始化
- 使用指向部分专用函数成员的指针自动填充向量
- 指向函数成员的指针