我有两个与C ++相关的问题:
在许多教科书中,关键字this
是指向调用对象的指针。正确?
由于我喜欢编码,我编写了以下简单代码:
struct Base
{
void g();
virtual void f();
};
void Base::f() {
cout << "Base::f()" << endl;
}
void Base::g() {
cout << "Base::g()" << endl;
cout << "sizeof(*this) : " << sizeof(*this) << endl;
this->f();
}
struct Derived : public Base
{
int d;
void f();
};
void Derived::f()
{
cout << "Derived::f()" << endl;
}
int main()
{
Base a;
Derived b;
cout << "sizeof(a) : " << sizeof(a) << endl;
cout << "sizeof(b) : " << sizeof(b) << endl;
a.g();
b.g();
}
上面的代码产生以下输出:
sizeof(a) : 4
sizeof(b) : 8
Base::g()
sizeof(*this) : 4
Base::f()
Base::g()
sizeof(*this) : 4 // why 4 bytes not 8 bytes?????????
Derived::f()
如果this
指向调用对象,那么sizeof(*this)
的第二行是否应该打印8而不是4,因为调用对象是b
?这里到底发生了什么? this
已被降级了吗?!!!!
如果将this
降级为Base
,this->f()
如何调用正确的函数?我真的很困惑。
答案 0 :(得分:12)
void Base::g() {
cout << "Base::g()" << endl;
cout << "sizeof(*this) : " << sizeof(*this) << endl;
this->f();
}
需要做的重要区别是sizeof
是编译时运算符,而不是运行时运算符。编译器将表达式sizeof(*this)
解释为“this
指向的对象的大小”,在Base::g
的范围内,它将是Base
类型的对象。编译器基本上会重写该语句,因为它知道Base
的大小是四个字节:
cout << "sizeof(*this) : " << 4 << endl;
答案 1 :(得分:1)
Base
无法查看/访问/了解派生对象的任何内容,因此sizeof
仅报告对象可见的部分。更重要的是,sizeof
方法中的Base
无法知道是否存在子类(您可以继承Base
而不重新编译它,所以它可以'报告除了它知道的部分之外的任何事情。 (sizeof
在编译时计算,而不是在运行时计算。)
答案 2 :(得分:0)
this
是一个常量值指针,指向该函数是非静态成员的对象。这意味着,要使this
成为可行值,它必须仅用于类的非静态成员。请记住:您必须使用对象实例来调用非静态成员函数(instance.function或instance-&gt;函数); this
是指向“实例”的指针。
大小永远不是你期望的8是因为g
是类Base
的成员。对于g
,this
的类型为Base *const
,因此*this
的类型为Base&
。 sizeof(Base)
是4.即使它是虚拟成员,也不会改变; g
实施的类型始终为Base *const
。实际上被覆盖的版本将具有不同的类型,但只有实现它们的类的类型。
this
的类型不遵循多态性;它完全和只定义函数的类型。
答案 3 :(得分:0)
调用正确的函数f
是因为Base::f
是虚拟的。这告诉编译器当请求调用Base*->f()
时,在您调用其成员的实际对象的vtable中查找被调用者的实际地址。
有问题的this
的类型是Base*
,这就是sizeof(*this) == sizeof(Base)
的原因,但它的vtable属于派生对象,因此函数调用到f
转到覆盖。