C++结构体多态调用异常:子类虚函数未触发问题咨询
inflate()没被调用?全是对象切片和指针用错惹的祸! 嘿,这个问题其实是C++里挺经典的新手坑——对象切片加上指针使用错误导致的,咱们一步步拆解来看:
首先看你代码里最致命的错误:你创建了栈上的Label对象,但往vector<Widget*>里塞的时候,直接传了对象本身Label1,而不是对象的地址&Label1。这操作直接触发了对象切片:当你把Label对象赋值给Widget类型时,子类独有的部分会被"切掉",只保留父类的成员,然后编译器会悄悄生成一个临时的Widget对象,把这个临时对象的地址塞进vector。所以你后续调用inflate()时,其实是在调用这个临时Widget对象的方法,完全和你创建的Label对象没关系,自然不会触发多态。
另外还有个隐藏的坑:就算你不小心写成了push_back(&Label1),把栈对象的地址塞进去,一旦这些栈对象出了当前作用域(比如函数结束),vector里的指针就变成野指针了,调用方法会直接导致未定义行为,程序崩给你看都有可能。
怎么改才能让多态正常工作?
给你几个靠谱的方案:
1. 用动态分配的对象(传统写法)
用new来创建Label对象,这样存进vector的是真正的子类对象指针,多态就能正常触发了:
#include <iostream> #include <vector> struct Widget { virtual void inflate(); }; struct Label : public Widget { void inflate(); }; void Label::inflate(){ std::cout << "Child function called" << std::endl; } void Widget::inflate(){ std::cout << "Parent function called" << std::endl; } int main() { std::vector<Widget*> widgets; // 动态分配Label对象 Label* label1 = new Label(); Label* label2 = new Label(); widgets.push_back(label1); widgets.push_back(label2); int i = 0; while(i < widgets.size()){ widgets[i]->inflate(); // 现在会输出"Child function called"两次 i++; } // 别忘了手动释放内存,不然会内存泄漏 for(auto w : widgets) { delete w; } return 0; }
2. 用智能指针(更安全,推荐)
在C++11及以后,强烈推荐用std::unique_ptr或者std::shared_ptr来管理动态内存,不用手动写delete,智能指针会自动帮你释放:
#include <iostream> #include <vector> #include <memory> // 记得加这个头文件 struct Widget { virtual void inflate(); }; struct Label : public Widget { void inflate(); }; void Label::inflate(){ std::cout << "Child function called" << std::endl; } void Widget::inflate(){ std::cout << "Parent function called" << std::endl; } int main() { std::vector<std::unique_ptr<Widget>> widgets; widgets.push_back(std::make_unique<Label>()); widgets.push_back(std::make_unique<Label>()); // 用范围for循环更简洁 for(auto& w : widgets) { w->inflate(); // 同样会正确调用子类方法 } // 啥都不用干,智能指针会在vector销毁时自动释放内存 return 0; }
3. 修正原代码的指针问题(不推荐,仅限临时测试)
如果你非要用栈对象,得确保对象在vector指针的生命周期内都活着,并且传入对象的地址:
#include <iostream> #include <vector> struct Widget { virtual void inflate(); }; struct Label : public Widget { void inflate(); }; void Label::inflate(){ std::cout << "Child function called" << std::endl; } void Widget::inflate(){ std::cout << "Parent function called" << std::endl; } int main() { std::vector<Widget*> widgets; Label label1; // 声明栈对象 Label label2; // 传入栈对象的地址,而不是对象本身 widgets.push_back(&label1); widgets.push_back(&label2); for(auto w : widgets) { w->inflate(); // 这时候就能调用Label的inflate()了 } // 注意:label1和label2不能提前被销毁(比如不能在这个代码块结束前就出作用域),否则指针就失效了 return 0; }
再啰嗦两句对象切片
你之前的代码里widgets.push_back(Label1)这行,编译器为啥会生成临时Widget对象?因为vector的元素是Widget*,编译器需要把Label对象转换成Widget*。这个转换过程会先把Label1切片成Widget类型(只保留父类的部分),生成一个临时的Widget实例,然后取这个临时对象的地址存进vector。所以vector里的指针指向的根本不是你创建的Label对象,而是个临时的Widget,调用方法当然是父类的了。
另外提一句,你原代码里Label1 = Label();这种写法是语法错误,得先声明变量类型:Label Label1;或者Label Label1 = Label();才行。
内容的提问来源于stack exchange,提问作者AlgoRythm

