const std::shared_ptr<const T> 作为函数的参数最终会改变智能指针中 class 的值
const std::shared_ptr<const T> as argument of a function ends up changing the values of the class in the smart pointer
我在开发一个小代码时遇到了一些问题。它本质上是一个几何库,我在其中为 3D 点定义了一个 class,为 3D 表面定义了一个抽象的 class,然后我实现了不同的表面(平面、圆锥...)。该库是在模板中构建的(它必须是这样我才能在 ceres-solver). The MCVE 中使用它是这样的:
point.h
#ifndef POINT_H
#define POINT_H
#include <iostream>
#include <memory>
template <typename T, int n>
class Surface;
template <typename T>
class Point
{
public:
typedef std::shared_ptr<Point<T>> Ptr;
typedef std::shared_ptr<const Point<T>> ConstPtr;
Point(T&& x, T&& y, T&& z)
{
data_[0] = x;
data_[1] = y;
data_[2] = z;
std::cout << "Point constructor" << std::endl;
}
Point(const T* data)
{
std::cout << "Cosntructor point from data ptr" << std::endl;
for (size_t i = 0; i < 4; i++)
data_[i] = data[i];
}
Point(const Point<T>& other) : Point(other.data_)
{
std::cout << "Constructor copy point" << std::endl;
}
~Point()
{
std::cout << "Point destructor" << std::endl;
}
template <int n>
T distance(const std::shared_ptr<const Surface<T, n>> surface) const
{
std::cout << "\n1: " << *this << std::endl;
ConstPtr a(this);
std::cout << "2: " << *this << std::endl;
T d = surface->distance(a);
std::cout << "3: " << *this << std::endl;
return d;
}
T& operator[](size_t i)
{
return data_[i];
}
T operator[](size_t i) const
{
return data_[i];
}
T& x()
{
std::cout << "x&" << std::endl;
return data_[0];
}
T& y()
{
return data_[1];
}
T& z()
{
return data_[2];
}
T x() const
{
return data_[0];
}
T y() const
{
return data_[1];
}
T z() const
{
return data_[2];
}
private:
T data_[3];
};
template <typename T>
std::ostream& operator<<(std::ostream& os, const Point<T>& point)
{
os << "[";
for (size_t i = 0; i < 3; i++)
{
if (i != 0)
{
os << ",";
}
os << point[i];
}
os << "]";
return os;
}
#endif // POINT_H
surface.h
#ifndef SURFACE_H
#define SURFACE_H
#include <iostream>
#include <memory>
template <typename T>
class Point;
template <typename T, int n>
class Surface
{
public:
typedef std::shared_ptr<Surface<T, n>> Ptr;
typedef std::shared_ptr<const Surface<T, n>> ConstPtr;
Surface()
{
std::cout << "Surface constructor" << std::endl;
}
virtual ~Surface()
{
std::cout << "Surface destructor" << std::endl;
}
virtual T distance(const std::shared_ptr<const Point<T>> point) const = 0;
T& operator[](size_t i)
{
return data_[i];
}
T operator[](size_t i) const
{
return data_[i];
}
protected:
T data_[n];
};
#endif // SURFACE_H
plane.h
#ifndef PLANE_H
#define PLANE_H
#include "surface.h"
#include <math.h>
template <typename T>
class Point;
template <typename T>
class Plane : public virtual Surface<T, 4>
{
public:
typedef std::shared_ptr<Plane<T>> Ptr;
typedef std::shared_ptr<const Plane<T>> ConstPtr;
using Surface<T, 4>::data_;
Plane(T&& nx, T&& ny, T&& nz, T&& d)
{
data_[0] = nx;
data_[1] = ny;
data_[2] = nz;
data_[3] = d;
std::cout << "Plane constructor" << std::endl;
}
Plane(const T* data)
{
std::cout << "Cosntructor plane from data ptr" << std::endl;
for (size_t i = 0; i < 4; i++)
data_[i] = data[i];
}
Plane(const Plane<T>& other) : Plane(other.data_)
{
std::cout << "Constructor copy plane" << std::endl;
}
virtual ~Plane()
{
std::cout << "Plane destructor" << std::endl;
}
virtual T distance(const std::shared_ptr<const Point<T>> point) const override
{
return (data_[0] * point->x() + data_[1] * point->y() + data_[2] * point->z() - data_[3]) /
sqrt(pow(data_[0], 2) + pow(data_[1], 2) + pow(data_[2], 2));
}
};
template <typename T>
std::ostream& operator<<(std::ostream& os, const Plane<T>& plane)
{
os << plane[0] << "*x+" << plane[1] << "*y+" << plane[2] << "*z = " << plane[3];
return os;
}
#endif // PLANE_H
主要
#include "plane.h"
#include "point.h"
int main()
{
Point<float>::Ptr point(new Point<float>(100, 100, 1.6));
Plane<float>::Ptr plane(new Plane<float>(1, 2, 3, -10));
float d1 = plane->distance(point);
std::cout << "Distance from " << *point << " to " << *plane << " -> " << d1 << std::endl;
std::cout << "Point " << *point << "; Plane " << *plane << std::endl;
float d2 = point->distance<4>(plane);
std::cout << "Distance from " << *point << " to " << *plane << " -> " << d2 << std::endl;
std::cout << "Point " << *point << "; Plane " << *plane << std::endl;
return 0;
}
这会产生正确的距离输出。但是,如果我使用 Point::distance
方法,则点的 x
和 y
值将更改为 0
,而如果我使用 Plane::distance
,则 x
和 y
的点没有改变。在这两种情况下,distance
方法都是常量,并具有输入 const std::shared_ptr<const Plane<T>>
和 const std::shared_ptr<const Point<T>>
。
我的问题:
如果所有内容都设置为 const
,怎么可能更改 Point 的值?
我知道当 Point::distance
中的 ConstPtr a(this)
被破坏时会发生这种情况,但我什至不知道哪个构造函数被用来创建 ConstPtr a(this)
Ponit::distance
方法。
程序的输出是:
Point constructor
Surface constructor
Plane constructor
Distance from [100,100,1.6] to 1*x+2*y+3*z = -10 -> 84.1338
Point [100,100,1.6]; Plane 1*x+2*y+3*z = -10
1: [100,100,1.6]
2: [100,100,1.6]
3: [100,100,1.6]
Point destructor
Distance from [0,0,1.6] to 1*x+2*y+3*z = -10 -> 84.1338
Point [0,0,1.6]; Plane 1*x+2*y+3*z = -10
Plane destructor
Surface destructor
Point destructor
问题在于 ConstPtr a(this);
- 此行创建了一个 std::shared_ptr
获取当前对象的所有权,并在它超出范围后 delete
对其进行处理。
因此,point
指针在调用 float d2 = point->distance<4>(plane);
后变为 悬空指针 并取消引用它会导致 未定义的行为
为了解决这个问题 Point
class 需要从 std::enable_shared_from_this
继承并且行 ConstPtr a(this);
需要变成 ConstPtr a(shared_from_this());
我在开发一个小代码时遇到了一些问题。它本质上是一个几何库,我在其中为 3D 点定义了一个 class,为 3D 表面定义了一个抽象的 class,然后我实现了不同的表面(平面、圆锥...)。该库是在模板中构建的(它必须是这样我才能在 ceres-solver). The MCVE 中使用它是这样的:
point.h
#ifndef POINT_H
#define POINT_H
#include <iostream>
#include <memory>
template <typename T, int n>
class Surface;
template <typename T>
class Point
{
public:
typedef std::shared_ptr<Point<T>> Ptr;
typedef std::shared_ptr<const Point<T>> ConstPtr;
Point(T&& x, T&& y, T&& z)
{
data_[0] = x;
data_[1] = y;
data_[2] = z;
std::cout << "Point constructor" << std::endl;
}
Point(const T* data)
{
std::cout << "Cosntructor point from data ptr" << std::endl;
for (size_t i = 0; i < 4; i++)
data_[i] = data[i];
}
Point(const Point<T>& other) : Point(other.data_)
{
std::cout << "Constructor copy point" << std::endl;
}
~Point()
{
std::cout << "Point destructor" << std::endl;
}
template <int n>
T distance(const std::shared_ptr<const Surface<T, n>> surface) const
{
std::cout << "\n1: " << *this << std::endl;
ConstPtr a(this);
std::cout << "2: " << *this << std::endl;
T d = surface->distance(a);
std::cout << "3: " << *this << std::endl;
return d;
}
T& operator[](size_t i)
{
return data_[i];
}
T operator[](size_t i) const
{
return data_[i];
}
T& x()
{
std::cout << "x&" << std::endl;
return data_[0];
}
T& y()
{
return data_[1];
}
T& z()
{
return data_[2];
}
T x() const
{
return data_[0];
}
T y() const
{
return data_[1];
}
T z() const
{
return data_[2];
}
private:
T data_[3];
};
template <typename T>
std::ostream& operator<<(std::ostream& os, const Point<T>& point)
{
os << "[";
for (size_t i = 0; i < 3; i++)
{
if (i != 0)
{
os << ",";
}
os << point[i];
}
os << "]";
return os;
}
#endif // POINT_H
surface.h
#ifndef SURFACE_H
#define SURFACE_H
#include <iostream>
#include <memory>
template <typename T>
class Point;
template <typename T, int n>
class Surface
{
public:
typedef std::shared_ptr<Surface<T, n>> Ptr;
typedef std::shared_ptr<const Surface<T, n>> ConstPtr;
Surface()
{
std::cout << "Surface constructor" << std::endl;
}
virtual ~Surface()
{
std::cout << "Surface destructor" << std::endl;
}
virtual T distance(const std::shared_ptr<const Point<T>> point) const = 0;
T& operator[](size_t i)
{
return data_[i];
}
T operator[](size_t i) const
{
return data_[i];
}
protected:
T data_[n];
};
#endif // SURFACE_H
plane.h
#ifndef PLANE_H
#define PLANE_H
#include "surface.h"
#include <math.h>
template <typename T>
class Point;
template <typename T>
class Plane : public virtual Surface<T, 4>
{
public:
typedef std::shared_ptr<Plane<T>> Ptr;
typedef std::shared_ptr<const Plane<T>> ConstPtr;
using Surface<T, 4>::data_;
Plane(T&& nx, T&& ny, T&& nz, T&& d)
{
data_[0] = nx;
data_[1] = ny;
data_[2] = nz;
data_[3] = d;
std::cout << "Plane constructor" << std::endl;
}
Plane(const T* data)
{
std::cout << "Cosntructor plane from data ptr" << std::endl;
for (size_t i = 0; i < 4; i++)
data_[i] = data[i];
}
Plane(const Plane<T>& other) : Plane(other.data_)
{
std::cout << "Constructor copy plane" << std::endl;
}
virtual ~Plane()
{
std::cout << "Plane destructor" << std::endl;
}
virtual T distance(const std::shared_ptr<const Point<T>> point) const override
{
return (data_[0] * point->x() + data_[1] * point->y() + data_[2] * point->z() - data_[3]) /
sqrt(pow(data_[0], 2) + pow(data_[1], 2) + pow(data_[2], 2));
}
};
template <typename T>
std::ostream& operator<<(std::ostream& os, const Plane<T>& plane)
{
os << plane[0] << "*x+" << plane[1] << "*y+" << plane[2] << "*z = " << plane[3];
return os;
}
#endif // PLANE_H
主要
#include "plane.h"
#include "point.h"
int main()
{
Point<float>::Ptr point(new Point<float>(100, 100, 1.6));
Plane<float>::Ptr plane(new Plane<float>(1, 2, 3, -10));
float d1 = plane->distance(point);
std::cout << "Distance from " << *point << " to " << *plane << " -> " << d1 << std::endl;
std::cout << "Point " << *point << "; Plane " << *plane << std::endl;
float d2 = point->distance<4>(plane);
std::cout << "Distance from " << *point << " to " << *plane << " -> " << d2 << std::endl;
std::cout << "Point " << *point << "; Plane " << *plane << std::endl;
return 0;
}
这会产生正确的距离输出。但是,如果我使用 Point::distance
方法,则点的 x
和 y
值将更改为 0
,而如果我使用 Plane::distance
,则 x
和 y
的点没有改变。在这两种情况下,distance
方法都是常量,并具有输入 const std::shared_ptr<const Plane<T>>
和 const std::shared_ptr<const Point<T>>
。
我的问题:
如果所有内容都设置为 const
,怎么可能更改 Point 的值?
我知道当 Point::distance
中的 ConstPtr a(this)
被破坏时会发生这种情况,但我什至不知道哪个构造函数被用来创建 ConstPtr a(this)
Ponit::distance
方法。
程序的输出是:
Point constructor
Surface constructor
Plane constructor
Distance from [100,100,1.6] to 1*x+2*y+3*z = -10 -> 84.1338
Point [100,100,1.6]; Plane 1*x+2*y+3*z = -10
1: [100,100,1.6]
2: [100,100,1.6]
3: [100,100,1.6]
Point destructor
Distance from [0,0,1.6] to 1*x+2*y+3*z = -10 -> 84.1338
Point [0,0,1.6]; Plane 1*x+2*y+3*z = -10
Plane destructor
Surface destructor
Point destructor
问题在于 ConstPtr a(this);
- 此行创建了一个 std::shared_ptr
获取当前对象的所有权,并在它超出范围后 delete
对其进行处理。
因此,point
指针在调用 float d2 = point->distance<4>(plane);
后变为 悬空指针 并取消引用它会导致 未定义的行为
为了解决这个问题 Point
class 需要从 std::enable_shared_from_this
继承并且行 ConstPtr a(this);
需要变成 ConstPtr a(shared_from_this());