为什么"this"在为派生类对象调用时无法访问基类方法中的派生类成员
Why `this` can't access the derived class members from base class methods when called for derived class object
This
指针在非常数成员函数中具有ClassName
类型。
class Base
{
public:
void get()
{
//this->put(); Why can't I call back a derived class method eventhough
**this** is pointing to derived class object.
}
};
class derived: public Base
{
public:
void put()
{
// do somthing.
}
};
int main()
{
derived d;
//d.get();
d.put();
return 0;
}
如果我在两个函数中打印this
指针的值,它是相同的,表明它是为派生类对象调用的。这里的this
指针类型也是derived *
。
同样,正如我所理解的,如果你在调用它的方法时有一个pointer
要对象,那么你只是指向offset
,从pointer
到object
中的address
开始,该方法存在于整个object layout
中。
但是,当我在base
类方法中有(derived)object
的start
地址时,为什么我不能偏移到derived
类方法。
我不明白为什么我不能这样做,因为上面的理解。我错过了一些非常基本的东西。
让我们想象一下你是一个编译器。现在你看到这个类:
class foo
{
void bar() { this->xyz(); }
};
你是做什么的?您抱怨不知道xyz
是什么,而且它肯定不是foo
的成员函数。您不需要四处查找其他类,也不需要检查它们是否可能从foo
派生并声明此函数——它只能以相反的方式工作。
另一种方法是,您必须声明希望派生类实现的方法的签名:
class foo
{
void bar() { this->xyz(); }
virtual void xyz() = 0;
};
突然间,这是有效的,但现在您不能再创建foo
的实例了:每个具有纯虚拟方法的类都是一个抽象类。
但是,请注意,确实适用于模板:
template <typename T>
class foo
{
void bar() { t.xyz(); }
T t;
};
因为每个模板都是在编译时实例化的,所以你不会看上面的模板,而是看可能提供这样一个函数的foo<xyz_class>
。
Me:编译器编译Base::get函数时,无法看到Derived::put函数。
您:派生的::不是放在同一个文件中吗?为什么编译器看不到?
我:如果4年后有人在另一个文件中从Base派生出Derived1::puttttt,该怎么办?
你:嗯,也许我能理解。
您必须将方法put()声明为虚拟的。
class Base
{
public:
virtual void put() = 0;
void get()
{
//this->put(); Why can't I call back a derived class method eventhough
**this** is pointing to derived class object.
}
};
class derived: public Base
{
public:
void put()
{
// do somthing.
}
};
int main()
{
derived d;
//d.get();
d.put();
return 0;
}
类Base
不知道您只将其用作Derived
对象的一部分;通常,可能有许多不同的派生类,其中只有一个或一些可能具有put()
。那么Base
是怎么编译的呢?
当您有一个指向Derived
对象的Base
指针时,可以强制转换以获得指向Derived
的指针,并以这种方式调用方法:
Base* b = new Derived;
dynamic_cast<Derived*>(b)-> put();
如果需要,类实际上可以将this
强制转换为派生实例ponter:
dynamic_cast<Derived*>(this)-> put();
您发布的代码将不会编译,因为编译器在为class Base
编译代码时不知道derived
类是什么。CCD_ 31指针是隐式传递给类的每个非静态成员函数的指针,以及该函数如何访问其实例的成员属性。在Base
类中,成员函数this
的类型为Base *
在const
成员函数中,它的类型为const Base *
。
此外,this
是一个非l值,这意味着你不能给它赋值
以下是关于这个指针的C++标准:
9.3.2该指针
在非静态(9.3)成员函数的主体中,关键字this是一个prvalue表达式,其值是为其调用函数的对象的地址。的成员函数中的类型类X是X*。如果成员函数被声明为const,则其类型为const X*,如果成员函数被声明为volatile,它的类型是volatile X*,如果成员函数被声明const volatile,其类型为const volabile X*。
现在,关于在你的问题中做你想做的事情,下面编译
#include <iostream>
class Base
{
public:
void get();
};
class derived: public Base
{
public:
void put()
{
std::cout << "This is a bad idea" << std::endl;
}
};
void Base::get()
{
//compiler knows what dervied class is
static_cast<derived *>(this)->put();
}
int main()
{
derived d;
d.get();
return 0;
}
输出为:This is a bad idea
我建议使用虚拟函数或模板来获得所需的行为。
考虑您已经在基类中编写了this->put()
方法,在编译期间,编译器会检查同一类中的put()函数,因为您没有任何这样的函数,它会显示编译时错误。
这是一个很老的问题,通过使用虚拟方法和重载给出的示例,这肯定仍然是正确的方法;然而,我认为值得一提的是,对于路人来说,由于";显式对象参数";(也称为"推导这个"):
#include <concepts>
#include <iostream>
class Derived;
class Base
{
public:
template <class Self>
requires std::same_as<Self, Derived>
void get(this Self& self)
{
self.put();
}
template <class Self>
void get(this Self& self)
{
std::cout << "Base" << std::endl;
}
};
class Derived: public Base
{
public:
void put()
{
std::cout << "Derived" << std::endl;
}
};
int main()
{
Base b;
Derived d;
b.get();
d.get();
return 0;
}
// Output:
// -------
// Base
// Derived
编译器资源管理器上使用if constexpr
而非requires
的版本
这对于基类实现具有与派生类型的对象(如CRTP)进行实际业务处理的情况非常有用。例如,该功能的作者之一Sy Brand在其关于该主题的开发博客中展示了如何使用显式对象参数来实现CRTP的相同结果,并可以说是更干净的实现。
- 绑定派生类方法C++从实例范围之外的分隔 std::function 变量调用
- 从基类实例调用派生类方法而不进行强制转换
- C++ 使用派生类方法更改基类数据成员
- 将派生类方法与基类unique_ptr一起使用
- 使用派生类C++方法的超类
- 从库类扩展,然后如何让库调用派生类方法
- 当应该调用派生类方法时,为什么要调用基类方法
- 抽象类的需求是什么?为什么要通过其基类访问派生类方法?在C++
- 如何轻松地将 Base 方法重定向到相同的派生类方法
- Gmock Base类方法中的派生类方法
- 如何调用精确派生类方法?
- 为什么可以从实例化基类对象的投射指针调用非静态派生类方法
- C++使用基类指针访问派生类方法
- 调用派生类方法时出现分段错误
- 从基类析构函数调用派生类方法
- 基类的指针数组:如何调用唯一的派生类方法
- 派生类方法的默认参数的最佳实践
- 从基类实例调用派生类方法
- C++ 继承:指向基类的派生类指针调用派生类方法
- 虚函数未解析为大多数派生类方法