我的基本问题很少,如果有人能清楚这一点,我会感激不尽。
1 :当我说base * b时,这是什么意思 =新派生的;为什么一个人会这样做?我们很好地分开可以 为类库创建对象 类派生然后调用 相应的功能。我知道 这个基数* b =新派生的;是 称为对象切片,但为什么和 什么时候会这样做?
2 :我知道 为什么不建议转换 要派生的基类对象 class对象(因为基类是 不知道派生类 成员和方法)。我甚至读过 其他StackOverflow线程如果 这就是我们的情况 必须改变/重新访问我们的设计。 但我知道这一切 只是好奇,有什么办法可以做 此?
class base
{
public:
void f(){cout << "In Base";}
};
class derived:public base
{
public:
void f(){cout << "In Derived";}
};
int _tmain(int argc, _TCHAR* argv[])
{
base b1, b2;
derived d1, d2;
b2 = d1;
d2 = reinterpret_cast<derived*>(b1); //gives error C2440
b1.f(); // Prints In Base
d1.f(); // Prints In Derived
b2.f(); // Prints In Base
d1.base::f(); //Prints In Base
d2.f();
getch();
return 0;
}
3:如果是上面的例子,有没有办法可以使用派生类对象调用基类f()?我使用了d1.base():: f()我只想知道是否有任何方法不使用范围解析运算符?
非常感谢您抽出时间帮助我!
答案 0 :(得分:5)
1。这不是对象切片:
base *b = new derived;
这是将base
类型的指针分配给derived
的实例。
其中一个(非常常见)的例子就是回调。考虑一下:
class Observer
{
public:
virtual void OnEvent() = 0;
};
class Subject
{
public:
Subject() : observer(0) {}
void SetObserver(Observer* o) { observer = o; }
void FireEvent() { if(observer != 0) observer->OnEvent(); }
private:
Observer* observer;
};
class MyObserver : public Observer
{
public:
void OnEvent() { ::printf("Hi there!\n"); }
};
int main()
{
Subject s;
MyObserver o;
s.SetObserver(&o);
s.FireEvent();
return 0;
}
这应该是预期的输出:
你好!
注意这里发生了什么。我们传入指向MyObserver
到SetObserver()
的实例的指针,即使该函数只接受Observer
类型的指针。这是有效的,因为MyObserver
(公开)来自Observer
。在这种情况下,我们说MyObserver
是一个 Observer
。
Observer
类型定义纯虚函数(=0
表示函数是纯函数;它必须由派生类实现)。 virtual
关键字告诉编译器调用该函数应该导致执行派生最多的函数。 OnEvent()
中的MyObserver
函数是派生最多的函数,因此,即使我们在OnEvent()
类型的指针上调用Observer
,也会调用该版本。
我们经历了这样做的麻烦,因为在这段代码中Subject
不必知道其观察者的确切类型 - 观察者只需从Observer
和{{{{}}派生出来1}}实例将调用最派生类型的Subject
实现。这允许代码解耦 - OnEvent()
不依赖于Subject
,而MyObserver
不依赖于MyObserver
。
2。将指针从基础转换为派生类型本身并没有错。以下实际上是合法的,并保证可行:
Subject
然而,此片段中return语句之前的行未定义:
class Base {};
class Derived : public Base {};
int main()
{
Derived d;
Base* bp = &d;
Derived* dp = static_cast<Derived*>(bp);
return 0;
}
class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};
int main()
{
Derived1 d1;
Base* bp = &d1;
Derived2* d2p = static_cast<Derived2*>(bp); // WTF?!
return 0;
}
指针的值没有意义,尝试访问其中的任何内容肯定会导致崩溃,因为d2p
指针实际上并不指向bp
实例,它指向Derived2
实例。编译器无法在编译时捕获它,因为Derived1
和Derived1
都继承自Derived2
,因此转换成功编译。这是从基类转换为派生类型的主要危险 - 如果转换实际上返回有意义的结果,您将无法知道直到运行时。
当然,除非你使用Base
,否则演员会受到运行时惩罚。 dynamic_cast<>()
最多涉及指针算术。 static_cast<>()
强制指针采用不同的(可能不相关的)类型,而不执行任何指针算法。这使得reinterpret_cast<>()
成为更危险的演员之一,并且只在必要时才使用,特别是如果reinterpret_cast<>()
可以完成这项工作。
3。请考虑以下事项:
static_cast<>()
如果class Base
{
public:
void Foobar() { ::printf("In Base!\n"); }
};
class Derived : public Base
{
public:
void Foobar() { ::printf("In Derived!\n"); }
};
int main()
{
Derived d;
Derived* dp = &d;
Base* bp = dp;
dp->Foobar();
bp->Foobar();
return 0;
}
功能不是虚拟的,那么您将获得此输出:
Foobar()
否则,如果In Derived!
In Base!
函数是虚拟的,那么您将获得此输出:
Foobar()
为了保证对虚函数In Derived!
In Derived!
的调用通过基指针调用基本实现,那么你必须使用范围解析运算符:
Foobar()
答案 1 :(得分:0)
是和否。你可以走了
class derived: public base {
public: void f() { base::f(); }
};
我不太确定全球范围。
答案 2 :(得分:0)
问题1:你为什么要这样做:你可能不会直接这样做。发生这种情况的情况将是你所拥有的,例如,一个函数返回“某种基础*”而你不关心哪种类型。
问题2:从派生内部,您需要使用范围解析运算符来指定您要使用基类的实现。
注意:您尚未将您的函数声明为虚拟,因此您实际上并没有获得您期望的行为。
答案 3 :(得分:0)
base *b = new derived
创建指向派生类对象的基类指针。这允许您创建 is-a 关系。类派生是基础。有关详细信息,请参阅inhertance。
2 . d2 = reinterpret_cast<derived*>(b1);
您正在尝试将基类对象转换为派生类对象。但使用的演员操作是。它不正确。
如果您打算调用基类方法,则使用Base类指针。
Base* ptr = &b1;
ptr->f();
我建议您参考FAQ Lite。
答案 4 :(得分:0)
base *b = new derived;
(1)您有一个基类指针指向派生类对象。这在实现多态时使用,其中有几个派生类继承自公共基类。然后,如果函数是虚函数,则可以通过基类指针根据类型调用正确的派生类函数。
class base
{
public:
virtual void f(){ cout << "In Base" << endl; }
};
class derived:public base
{
public:
void f(){ cout << "In Derived" << endl; }
};
class derived2: public derived
{
public:
void f() { cout << "In Derived2" << endl; }
};
int _tmain(int argc, _TCHAR* argv[])
{
derived d1;
derived2 d2;
base* b;
b = &d1;
b->f(); // calls derived::f();
b = &d2;
b->f(); // calls derived2::f();
return 0;
}
它不称为对象切片。对象切片是指将派生类对象分配给基类对象(而不是指针),其中派生类对象的成员被切片,保持不变。因此,您无法从基类对象访问这些切片成员。
(2)除了使用reinterpret_cast的行之外,其他语句都没有问题。
(3)如果您正在使用对象或覆盖派生类函数来调用基类版本(如其他答案中所述),则必须使用范围解析运算符。但是,您可以使用指针:
derived *d = static_cast( &b1 );
d->f();