我想知道是否有可能(以及是否,如何)创建一个 X 值的指针
现在,假设我知道可以在这个指针中分配哪些类型。
例如,一个 X 值的指针(当然可以随意更改这个值的名称),它可以指向 string、bool 和自定义类的变量
8 回答
通常你所描述的是一个坏主意。
void*
作品,为作品的边际价值。它抛弃了所有类型的安全性,要求您跟踪它。
创建根类型是可行的,但它不适用于原始类型,而且相当侵入。
A是一个变量,可以包含指向这 3 种类型(或)boost::variant< bool*, std::string*, MyClass* >
中任何一种的指针。您可能会发现使用起来很有挑战性,因为它强制类型安全,而且语法会让人讨厌。bool
std::string
MyClass
同样,指向的指针boost::variant< bool, std::string, MyClass >
可能是您想要的,但它不会让您指向bool
您没有在胡闹的变量。
完整的 C++11 支持,union
可以包含任意类型,再加上一个枚举可以让你做一些非常像boost::variant
. 不利的一面是,这需要你被指出的东西是union
. 无论有没有完整的 C++11 支持,指针联合都是合理的。在这两种情况下,您都必须手动跟踪类型并保持同步。
您真正需要考虑的是“我为什么要问这个?”,因为与许多问题一样,动机很重要。你可能没有问正确的问题。
我今天学了一个新表达,所以我要用它,“这是一个XY问题”,你想做X,所以你认为解决方案是Y,所以你问怎么做Y。你可能应该问如何做 Y 代替。这有点像你发现你的汽车前轮被刺破了,你去和机械师谈谈,但你不问“我该如何修理爆胎”,你问“我如何解开车轮螺母”,而只是在您卸下所有车轮螺母并且汽车翻倒后,您是否意识到应该在汽车下方放置一个千斤顶,以便能够在汽车不会翻倒的情况下取下车轮...
当然,有 void 指针和联合,这没什么大不了的。但是,您仍然需要知道您的指针实际指向什么,否则您将一团糟。
所以一般来说,在 C++ 中,你可能不应该这样做。
您应该做的是将您的“事物”包装在另一个对象中,该对象知道内容是什么,并且可以处理它。这样,您就没有其他东西来维护对象是什么以及如何使用它的状态。
例如,我们可以这样:
class AnyThing
{
public:
virtual ~AnyThing();
virtual std::string ToString() = 0;
... // other things you want to do with your "things".
};
class IntThing: public AnyThing
{
private:
int value;
public:
virtual std::string ToString() { ... convert int to string ... }
};
class StringThing : public Anything
{
private:
std::string value;
public:
virtual std::string ToString() { return value; }
}
您可以使用指向 void的指针。
如果您想要一个只能用于指向这三种类型的指针,那么我会看到三个选项:
为从某个基类派生的每种类型创建一个包装类:
class Thingy { protected: Thing() {} }; class BoolThingy : public Thingy { bool x; } class StringThingy : public Thingy { String x; } class CustomThingy : public Thingy { Custom x; } ... Thingy *p = new BoolThingy;
创建一个智能指针类,重载接受
bool *
,String *
,Custom *
的赋值运算符,并重载运*
算符(虽然那会做什么,我不知道!)使用变体类(例如
boost::variant
)。
但是对于任何选项,尚不清楚这样的东西会有什么用......
void*
具有指向任何数据类型的属性。它实际上用作容器或柜子,您可以在其中放置任何数据类型的变量,然后将其传递void*
给函数。
您必须知道您传递的数据的数据类型,才能使用它。
例如:
int main()
{
int a;
void *x=&a;
func(x);
....
}
void func(void *argument)
{
int i=*(int *)argument;
....
}
不,您不能有一个指针指向未指定类型的对象。指针的全部意义在于它指向特定类型的对象。int*x
指向一个int
,A*a
指向一个A
。
当然,您可以做的是拥有一个void*
完全不指向任何内容的类型变量 ( void
),但可以保存任何地址。如果您以某种方式记住它的地址,那么您可以使用 astatic_cast<>
强制转换为适当的指针。或者,您可以使用dynamic_cast<>
在运行时找出您是否void*
指向给定类型。这可以实现如下
struct AnyPointerWrapper
{
struct null_pointer {};
struct wrong_pointer_type {};
AnyPointerWrapper() : ptr(0) {}
AnyPointerWrapper(AnyPointerWrapper const&) = default;
AnyPointerWrapper&operator=(AnyPointerWrapper const&) = default;
template<typename T>
explicit AnyPointerWrapper(T*p)
: ptr(p) {}
template<typename T>
AnyPointerWrapper&operator=(T*p)
{ ptr=p; return*this; }
bool is_null() const
{ return ptr==0; }
template<typename T>
bool is() const
{ return dynamic_cast<T*>(ptr) != 0; }
template<typename T>
T* pointer()
{
if(p==0) throw null_pointer;
T*p = dynamic_cast<T*>(ptr);
if(p==0) throw wrong_pointer_type;
return p;
}
private:
void*ptr;
};
并像这样使用
int X;
AnyPointerWrapper a;
assert(a.is_null());
a = &X;
assert(a.is<int>());
int*p = a.pointer<int>();
(您可以添加更多功能,包括对const
指针的支持)。但是请注意,这dynamic_cast<>
并非微不足道,即会导致一些性能损失。另请注意,这AnyPointerWrapper
不是指针:您不能使用->
运算符来调用地址存储在AnyPointerWrapper::ptr
;中的对象的成员函数。它只是一个包装器,您可以从中获得适当的指针。
您可以拥有指向任何类型(对象、指针、原始类型等)的指针,但不能拥有指向引用的指针。
如果您事先知道类型并且它们没有构造函数。然后你可以使用联合。
class CustomClass {};
union MyType
{
char const* a;
bool b;
float c;
};
MyType stuff;
MyType* ptrToStuff = &stuff;
int main()
{
ptrToStuff->a = "Plop";
ptrToStuff->b = false;
ptrToStuff->c = 12.0;
}
Boost 也有 ANY 类型。
你可以在里面存储任何东西。
boost::any x = 12;
x = new CustomClass;
x = new std::string("Hi There");
x = std::string("Plop"); // even works without pointers.