这可能已被问过一百万次,或者可能是非常愚蠢但为什么没有实现呢?
class A
{
public:
A(){ a = 5;}
int a;
};
class B:public A
{
public:
B(){ a = 0.5;}
float a;
};
int main()
{
A * a = new B();
cout<<a->a;
getch();
return 0;
}
此代码将访问A :: a。我如何访问B :: a?
答案 0 :(得分:35)
访问B :: a:
cout << static_cast<B*>(a)->a;
显式访问A :: a和B :: a:
cout << static_cast<B*>(a)->A::a;
cout << static_cast<B*>(a)->B::a;
(dynamic_cast有时比static_cast更好,但它不能在这里使用,因为A和B不是多态的。)
至于为什么C ++没有虚拟变量:虚函数允许多态;换句话说,他们通过调用代码让两个不同类型的类被视为相同,这两个类的内部行为的任何差异被封装在虚函数中。
虚拟成员变量真的没有意义;只需访问变量就没有封装行为。
还要记住,C ++是静态类型。虚函数允许您在运行时更改行为;您的示例代码尝试不仅在运行时更改行为,而且数据类型(A::a
为int
,B::a
为float
)和C ++不这样做。如果需要在运行时容纳不同的数据类型,则需要在虚拟函数中封装这些差异,以隐藏数据类型的差异。例如(仅限演示代码;对于实际代码,您需要overload operator<< instead):
class A
{
public:
A(){ a = 5;}
int a;
virtual void output_to(ostream& o) const { o << a; }
};
class B:public A
{
public:
B(){ a = 0.5;}
float a;
void output_to(ostream& o) const { o << a; }
};
另外请记住,像这样公开成员变量可能会破坏封装,并且通常不赞成。
答案 1 :(得分:10)
不公开数据,并通过虚函数访问它们。
请考虑一下,您要求的内容必须如何实施。基本上,它会强制对任何数据成员的任何访问都通过虚函数。请记住,您通过指向A对象的指针访问数据,而A类不知道您在B类中所做的事情。
换句话说,我们可以在任何地方访问任何数据成员 - 或者您可以编写虚拟方法。猜猜哪些C ++的设计师选择了......
答案 2 :(得分:4)
你不能这样做,而且C ++不支持它,因为它打破了基本的C ++原则。
float
与int
的类型不同,名称查找以及确定在编译时进行值分配所需的转换。然而,a->a
真正命名的包括其实际类型的内容只能在运行时知道。
您可以使用模板参数化类A
template<typename T>
class A
{
public:
// see also constructor initializer lists
A(T t){ a = t; }
T a;
};
然后你可以传递类型,但是只能在编译时传递上述原则的原因。
A<int> a(5);
A<float> b(5.5f);
答案 3 :(得分:3)
(dynamic_cast<B*>(a))->a
?
毕竟你为什么需要这个?虚拟功能还不够吗?
答案 4 :(得分:3)
不考虑virtual methods should be private的论点,虚拟方法旨在作为额外的封装层(封装行为的变化)。直接访问字段反对封装开始,因此制作虚拟字段会有点虚伪。由于字段不定义仅存储数据的行为,因此实际上没有任何虚拟化行为。你有一个公共int或float的事实是反模式。
答案 5 :(得分:2)
您可以向下转换变量以访问B::a
。
类似的东西:
((B*)a)->a
我认为在大多数OO编程语言中都是一样的。我想不出任何实施virtual variables
概念的人......
答案 6 :(得分:2)
您可以创建如下效果:
#include <iostream>
class A {
public:
double value;
A() {}
virtual ~A() {}
virtual void doSomething() {}
};
class B : public A {
public:
void doSomething() {
A::value = 3.14;
}
};
int main() {
A* a = new B();
a->doSomething();
std::cout << a->value << std::endl;
delete a;
return 0;
}
在上面的示例中,您可以说A的值与虚拟变量应具有的效果相同。
编辑:这是您问题的实际答案,但看到您的代码示例后,我注意到您正在寻找虚拟变量中的不同类型。你可以用这样的联合替换double value
:
union {
int intValue;
float floatValue;
} value
并像访问它一样:
a->value.intValue = 3;
assert(a->value.floatValue == 3);
请注意,出于速度原因,我会避免这种情况。
答案 7 :(得分:2)
因为根据C标准,类或结构中字段的偏移量必须是编译时常量。这也适用于访问基类字段时。
您的示例也不适用于虚拟getter,因为覆盖需要相同的类型签名。如果这是必要的,你的虚拟getter必须以代数类型返回,并且接收代码必须在运行时检查它是否是预期的类型。
答案 8 :(得分:1)
C ++不支持这种做法,因为它违反了封装原则。
您的类应该公开并实现一个公共(可能是虚拟的)接口,该接口告诉类用户没有关于您的类的内部工作。接口应描述类可以在抽象级别执行的操作(和结果),而不是“将此变量设置为X”。