通过此指针调用受保护的基类方法,该指针投射到派生类(C++)中的基类
Calling protected base class method via this pointer casted to base class in derived class (C++)
首先,我知道C++标准(ISO/IEC 14882:2003):第11.5节第1段,但事实并非如此(但编译器显然不这么认为)。
我试图通过这个指针调用派生类方法中的受保护基类方法,该指针静态转换为基类指针,并在MSVC2008中出现错误C2248:"A::f":无法访问类"A"中声明的受保护成员。
我有在"奇怪的重复模板模式"的上下文中这样做,但我可以在更简单的代码中重现这个错误,如下所示:
class B
{
protected:
void f(){}
};
class D : public B
{
public:
void g()
{
f(); // ok
this->f(); // ok
static_cast<B*>(this)->f(); // C2248 in MSVC2008
dynamic_cast<B*>(this)->f(); // C2248
((B*)this)->f(); // C2248
}
};
D d; d.g();
编译器似乎认为这个指针是指向其他实例的指针,是吗?
在这种情况下编译器是错误的,你认为呢?
好的,我的真实代码更像这样:
template<class T>
class B
{
public:
void g()
{
f(); // error C3861: 'f': identifier not found
this->f(); // error C3861: 'f': identifier not found
// static_cast to derived class
static_cast<T*>(this)->f(); // C2248 in MSVC2008
}
};
class D : public B<D>
{
protected:
void f(){}
};
我将this强制转换为派生的类,并且我不能使用this->f()
顺便说一句,我看到这段代码对于class E : public B<D> {...};
:compilable这样的用法是不安全的,但static_cast会进行错误的强制转换。
编译器是正确的。要显式访问B::f
成员函数,可以编写:
this->B::f();
相关语言为:
c++11
11.4受保护的成员访问[class.Protected]
[…]由于引用发生在某个成员的朋友或成员中,因此授予对受保护成员的访问权限class C.[…]访问受保护成员[…]涉及[s]一个(可能是隐式的)对象表达式(5.2.5)。在这种情况下,对象表达式的类应为C或从C派生的类。
因此,通过对基类B
的强制转换的受保护成员访问违反了该授权,并且被禁止。由于您可以如上所述使用this->B::f()
,因此也没有必要这样做。
在您实际的CRTP动机的情况下,您不能在没有static_cast
的情况下调用f()
,这是正确的,因为D
不是B<D>
的基类(继承关系是相反的)。由于D
不是B<D>
的基类,因此无论如何都不能从B<D>
调用其protected
方法。一个简单的解决方法是friend
B<D>
到D
,并在this
指针上使用static_cast
:
template<typename T>
class B {
public:
void g() {
static_cast<T *>(this)->f();
}
};
class D : public B<D>
{
friend class B<D>;
...
如果让B
访问D
的private
部分让您担心,您可以将private
部分移动到另一个基类,并隔离D
:中的CRTP机制
template<class T> class B {
public:
void g() {
static_cast<T*>(this)->f();
}
};
class C {
private:
void h();
protected:
void f(){ std::cout << "D::fn"; }
};
class D: protected C, public B<D>
{
friend class B<D>;
};
这里CCD_ 23被阻止调用CCD_ 24,因为友谊既不是继承的也不是传递的。
我认为编译器是对的。
假设如下:
void g()
{
B *b1 = this;
B *b2 = GetUnrelatedB();
b1->f(); //Error?
b2->f(); //Error!
}
b1
的情况相当于您的static_cast
,但非常奇怪的是,b1
将被允许,而b2
将不被允许。
引用你的第11.5段:
[…]访问必须通过指向派生类本身的指针、引用或对象。
但是static_cast<B*>(this)
的类型是B*
,而不是D*
,不管对象本身是相同的。实际上,指针的值与这个问题无关,只是表达式的类型:
void g()
{
B *b2 = GetUnrelatedB();
static_cast<D*>(b2)->f(); //ok!
}
但是,一旦在this
上应用了static_cast
,编译器如何知道您在从B派生的类中?在我(谦虚地)看来,如果我创建了一个B
对象,我希望不允许在B
对象上调用私有或受保护的B
方法,因为我们不想违反封装。B
对象在哪里创建并不重要,只要它在B
类方法之外即可。
- 如何使用基类指针引用派生类成员
- 使用基类指针创建对象时,缺少派生类析构函数
- 如果基类包含双指针成员,则派生类的构造函数
- 使用基类指针调用基类的值构造函数的语法是什么?
- 如何在基类指针向量的元素上应用重载的多态函数
- 如何使用 std::make_shared 创建基类类型的智能指针?
- 如何在不使用指针的情况下将派生类的对象作为参数传递给基类中的函数?
- 指向基类派生类的 std::unique_ptr 的指针
- 当目标指针不是基类的类型时,为什么允许dynamic_cast为多态类生成 null 指针?
- 如何在成为指向基类的指针后保留对子类方法的使用?
- 派生类是从基类继承 v 指针并仅使用它,还是也有自己的 v 指针?
- std::variant vs指向C++中异构容器基类的指针
- 模板化函数或具有指向基类的指针的函数
- 指向基类的唯一指针
- C++函数解析基类指针到派生类指针
- 指向基类的指针的 std::vector 的深层副本
- 在将派生类指针类型转换为派生类指针后,从基类指针调用派生类函数
- 如何将子类作为函数的参数传递给期望基类,然后将该对象传递到指向这些抽象类对象的指针向量中?
- 无法从指针派生类转换为指针基类(多态性)
- 使用指针基类的=操作符的多态性