虚函数c++的链接器错误
Linker error with virtual functions c++
我不确定这段代码有什么问题,我正在学习Builder pattern。示例代码在 Java 中,我试图在 C++ 中编写相同的代码,但出现链接器错误。我搜索并阅读了所有内容,但仍然找不到正确的方法,因此将其张贴在这里。如果我遗漏了一些非常微不足道的东西,我深表歉意。
#include <iostream>
#include <string>
#include <list>
#include <memory>
#include <conio.h>
using namespace std;
using std::string;
using std::unique_ptr;
using std::list;
class Packing
{
public:
virtual string pack() = 0;
};
template<typename T>
class Item
{
public:
virtual string name();
virtual Packing* packing();
virtual float price();
};
/*
As per comments, I have now defined the functions in my Item class, but
the actual definition is in the derived classes and because of these
definitions, I am not getting the actual output. I have provided the
required and actual output of this code.
I also read about CRTP and have incorporated those changes as well. But
still am not able to figure out how to get Items in the list.
*/
template<typename T>
string Item<T>::name()
{
return "Item Class";
}
template<typename T>
Packing* Item<T>::packing()
{
return (nullptr);
}
template<typename T>
float Item<T>::price()
{
return 0.0f;
}
class Wrapper : public Packing
{
public:
string pack() override
{
return "Wrapper";
}
};
class Bottle : public Packing
{
public:
string pack() override
{
return "Bottle";
}
};
class Burger : public Item<Burger>
{
public:
Packing* packing() override;
};
Packing* Burger::packing()
{
return (new Wrapper());
}
class ColdDrink : public Item<ColdDrink>
{
public:
Packing* packing() override;
};
Packing* ColdDrink::packing()
{
return (new Bottle());
}
class VegBurger : public Burger
{
public:
float price() override
{
return 25.0f;
}
string name() override
{
return "Veg Burger";
}
};
class ChickenBurger : public Burger
{
public:
float price() override
{
return 50.5f;
}
string name() override
{
return "Chicken Burger";
}
};
class Coke : public Burger
{
public:
float price() override
{
return 30.0f;
}
string name() override
{
return "Coke";
}
};
class Pepsi : public Burger
{
public:
float price() override
{
return 35.0f;
}
string name() override
{
return "Pepsi";
}
};
class Meal
{
public:
Meal() {}
void addItem(Item& item) // This is the error place after changing my
// code to use templates. The error is:
// 1>c:\users\xxx\documents\visual studio
//2015\projects\mealbuilder\mealbuilder\mealbuilder.h(14):
// error C2955: 'Item': use of class template
// requires template argument list
{
items.push_back(std::move(item));
}
float getCost()
{
float cost = 0.0f;
for (auto& item : items)
{
cost += item.price();
}
return cost;
}
void showItems()
{
for (auto& item : items)
{
cout << "Item : " << item.name() << endl;
cout << "Packing : " << item.packing() << endl;
cout << "Price : " << item.price() << endl << endl;
}
}
private:
list<Item> items;
};
class MealBuilder
{
public:
Meal prepareVegMeal()
{
Meal meal;
VegBurger vegBurger;
Coke coke;
meal.addItem(vegBurger);
meal.addItem(coke);
return meal;
}
Meal prepareNonVegMeal()
{
Meal meal;
ChickenBurger chickenBurger;
Pepsi pepsi;
meal.addItem(chickenBurger);
meal.addItem(pepsi);
return meal;
}
};
int main()
{
MealBuilder mealBuilder;
Meal vegMeal = mealBuilder.prepareVegMeal();
cout << "Veg Meal: " << endl;
vegMeal.showItems();
cout << "Total cost: " << vegMeal.getCost();
Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
cout << "Non-Veg Meal: " << endl;
nonVegMeal.showItems();
cout << "Total cost: " << nonVegMeal.getCost();
_getch();
return 0;
}
根据评论,这是我在添加项目定义之前遇到的错误 class:
1>------ Build started: Project: MealBuilder, Configuration: Debug Win32 ------
1> MealBuilder.cpp
1>MealBuilder.obj : error LNK2001: unresolved external symbol "public:
virtual class std::basic_string<char,struct std::char_traits<char>,class
std::allocator<char> > __thiscall Item::name(void)" (?name@Item@@UAE?AV?
$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ)
1>MealBuilder.obj : error LNK2001: unresolved external symbol "public:
virtual class Packing * __thiscall Item::packing(void)" (?
packing@Item@@UAEPAVPacking@@XZ)
1>MealBuilder.obj : error LNK2001: unresolved external symbol "public:
virtual float __thiscall Item::price(void)" (?price@Item@@UAEMXZ)
1>C:\Users\XXX\documents\visual studio
2015\Projects\MealBuilder\Debug\MealBuilder.exe : fatal error LNK1120: 3
unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
添加定义后,我得到以下输出:
Veg Meal:
Item : Item Class
Packing : 00000000
Price : 0
Item : Item Class
Packing : 00000000
Price : 0
Total cost: 0
Non-Veg Meal:
Item : Item Class
Packing : 00000000
Price : 0
Item : Item Class
Packing : 00000000
Price : 0
Total cost: 0
但要求的输出是:
Veg Meal
Item : Veg Burger, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0
Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50.5
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.5
我不确定如何更改我的代码以获得所需的输出。请帮忙。
非常感谢,提前。
错误在这些行中:
class MealBuilder
{
public:
Meal prepareVegMeal() <-- changed this line, removed reference
{
....
}
Meal prepareNonVegMeal() <-- changed this line, removed reference
{
....
}
};
修复此问题后,您将需要项目 class 中的成员函数。
原因:
在 C++ 中,非常量引用不能绑定到临时对象。 C++ 不希望您不小心修改临时对象。您正在使用 std::move 返回。 std::move 是对右值引用的转换。按照惯例,右值引用被视为 "references you are permitted to move the data out of, as the caller promises they really don't need that data anymore".
查看此答案了解更多详情。
How come a non-const reference cannot bind to a temporary object?
您的 Item
class 成员函数已声明但从未定义。如果您从未实例化此 class 或从它派生的 classes,您可能会侥幸逃脱。然而,这不应该被依赖。
虚函数可以是普通函数,也可以是纯虚函数。
class A {
virtual void func1() {};
virtual void func2() = 0;
}
类 至少有一个纯虚函数不能实例化,但可以使用指针和引用指向它。
必须定义声明的虚函数。
class B {
virtual func1();
}
int main() {
new B;
}
此代码会产生类似于您遇到的链接错误。
虽然您可能不会显式实例化 class,但一旦实例化从它派生的 class,您就会隐式实例化它:
class B {
virtual func1();
}
class C : B {
virtual func2() {}
}
int main() {
new C;
}
此代码会产生类似的链接错误。
回到你的案子。确保所有函数都是纯虚拟的或正确定义的。
您的代码中还有另一个错误。令人惊讶的是,编译器允许它通过。
在函数中
Meal& prepareVegMeal()
{
Meal meal;
//...
return std::move(meal);
}
您正在创建一个临时对象并return通过引用对其进行处理。这个临时对象在范围结束时自动销毁。返回临时对象的指针或引用是一个常见的错误,会导致内存损坏。大多数编译器可以轻松检测到它们。查看您在此处使用 std::move
来消除警告的尝试是错误的。 std::move
只是一种告诉程序员该对象正在从未指定状态移出并保留在未指定状态的方式。你真正想在这里做的是 return 对象的值:
Meal prepareVegMeal() // Notice no reference
{
Meal meal;
//...
return meal;
}
但要小心,因为虚函数调用不适用于值语义。
我不确定这段代码有什么问题,我正在学习Builder pattern。示例代码在 Java 中,我试图在 C++ 中编写相同的代码,但出现链接器错误。我搜索并阅读了所有内容,但仍然找不到正确的方法,因此将其张贴在这里。如果我遗漏了一些非常微不足道的东西,我深表歉意。
#include <iostream>
#include <string>
#include <list>
#include <memory>
#include <conio.h>
using namespace std;
using std::string;
using std::unique_ptr;
using std::list;
class Packing
{
public:
virtual string pack() = 0;
};
template<typename T>
class Item
{
public:
virtual string name();
virtual Packing* packing();
virtual float price();
};
/*
As per comments, I have now defined the functions in my Item class, but
the actual definition is in the derived classes and because of these
definitions, I am not getting the actual output. I have provided the
required and actual output of this code.
I also read about CRTP and have incorporated those changes as well. But
still am not able to figure out how to get Items in the list.
*/
template<typename T>
string Item<T>::name()
{
return "Item Class";
}
template<typename T>
Packing* Item<T>::packing()
{
return (nullptr);
}
template<typename T>
float Item<T>::price()
{
return 0.0f;
}
class Wrapper : public Packing
{
public:
string pack() override
{
return "Wrapper";
}
};
class Bottle : public Packing
{
public:
string pack() override
{
return "Bottle";
}
};
class Burger : public Item<Burger>
{
public:
Packing* packing() override;
};
Packing* Burger::packing()
{
return (new Wrapper());
}
class ColdDrink : public Item<ColdDrink>
{
public:
Packing* packing() override;
};
Packing* ColdDrink::packing()
{
return (new Bottle());
}
class VegBurger : public Burger
{
public:
float price() override
{
return 25.0f;
}
string name() override
{
return "Veg Burger";
}
};
class ChickenBurger : public Burger
{
public:
float price() override
{
return 50.5f;
}
string name() override
{
return "Chicken Burger";
}
};
class Coke : public Burger
{
public:
float price() override
{
return 30.0f;
}
string name() override
{
return "Coke";
}
};
class Pepsi : public Burger
{
public:
float price() override
{
return 35.0f;
}
string name() override
{
return "Pepsi";
}
};
class Meal
{
public:
Meal() {}
void addItem(Item& item) // This is the error place after changing my
// code to use templates. The error is:
// 1>c:\users\xxx\documents\visual studio
//2015\projects\mealbuilder\mealbuilder\mealbuilder.h(14):
// error C2955: 'Item': use of class template
// requires template argument list
{
items.push_back(std::move(item));
}
float getCost()
{
float cost = 0.0f;
for (auto& item : items)
{
cost += item.price();
}
return cost;
}
void showItems()
{
for (auto& item : items)
{
cout << "Item : " << item.name() << endl;
cout << "Packing : " << item.packing() << endl;
cout << "Price : " << item.price() << endl << endl;
}
}
private:
list<Item> items;
};
class MealBuilder
{
public:
Meal prepareVegMeal()
{
Meal meal;
VegBurger vegBurger;
Coke coke;
meal.addItem(vegBurger);
meal.addItem(coke);
return meal;
}
Meal prepareNonVegMeal()
{
Meal meal;
ChickenBurger chickenBurger;
Pepsi pepsi;
meal.addItem(chickenBurger);
meal.addItem(pepsi);
return meal;
}
};
int main()
{
MealBuilder mealBuilder;
Meal vegMeal = mealBuilder.prepareVegMeal();
cout << "Veg Meal: " << endl;
vegMeal.showItems();
cout << "Total cost: " << vegMeal.getCost();
Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
cout << "Non-Veg Meal: " << endl;
nonVegMeal.showItems();
cout << "Total cost: " << nonVegMeal.getCost();
_getch();
return 0;
}
根据评论,这是我在添加项目定义之前遇到的错误 class:
1>------ Build started: Project: MealBuilder, Configuration: Debug Win32 ------
1> MealBuilder.cpp
1>MealBuilder.obj : error LNK2001: unresolved external symbol "public:
virtual class std::basic_string<char,struct std::char_traits<char>,class
std::allocator<char> > __thiscall Item::name(void)" (?name@Item@@UAE?AV?
$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ)
1>MealBuilder.obj : error LNK2001: unresolved external symbol "public:
virtual class Packing * __thiscall Item::packing(void)" (?
packing@Item@@UAEPAVPacking@@XZ)
1>MealBuilder.obj : error LNK2001: unresolved external symbol "public:
virtual float __thiscall Item::price(void)" (?price@Item@@UAEMXZ)
1>C:\Users\XXX\documents\visual studio
2015\Projects\MealBuilder\Debug\MealBuilder.exe : fatal error LNK1120: 3
unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
添加定义后,我得到以下输出:
Veg Meal:
Item : Item Class
Packing : 00000000
Price : 0
Item : Item Class
Packing : 00000000
Price : 0
Total cost: 0
Non-Veg Meal:
Item : Item Class
Packing : 00000000
Price : 0
Item : Item Class
Packing : 00000000
Price : 0
Total cost: 0
但要求的输出是:
Veg Meal
Item : Veg Burger, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0
Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50.5
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.5
我不确定如何更改我的代码以获得所需的输出。请帮忙。
非常感谢,提前。
错误在这些行中:
class MealBuilder
{
public:
Meal prepareVegMeal() <-- changed this line, removed reference
{
....
}
Meal prepareNonVegMeal() <-- changed this line, removed reference
{
....
}
};
修复此问题后,您将需要项目 class 中的成员函数。
原因: 在 C++ 中,非常量引用不能绑定到临时对象。 C++ 不希望您不小心修改临时对象。您正在使用 std::move 返回。 std::move 是对右值引用的转换。按照惯例,右值引用被视为 "references you are permitted to move the data out of, as the caller promises they really don't need that data anymore".
查看此答案了解更多详情。
How come a non-const reference cannot bind to a temporary object?
您的 Item
class 成员函数已声明但从未定义。如果您从未实例化此 class 或从它派生的 classes,您可能会侥幸逃脱。然而,这不应该被依赖。
虚函数可以是普通函数,也可以是纯虚函数。
class A {
virtual void func1() {};
virtual void func2() = 0;
}
类 至少有一个纯虚函数不能实例化,但可以使用指针和引用指向它。
必须定义声明的虚函数。
class B {
virtual func1();
}
int main() {
new B;
}
此代码会产生类似于您遇到的链接错误。
虽然您可能不会显式实例化 class,但一旦实例化从它派生的 class,您就会隐式实例化它:
class B {
virtual func1();
}
class C : B {
virtual func2() {}
}
int main() {
new C;
}
此代码会产生类似的链接错误。
回到你的案子。确保所有函数都是纯虚拟的或正确定义的。
您的代码中还有另一个错误。令人惊讶的是,编译器允许它通过。
在函数中
Meal& prepareVegMeal()
{
Meal meal;
//...
return std::move(meal);
}
您正在创建一个临时对象并return通过引用对其进行处理。这个临时对象在范围结束时自动销毁。返回临时对象的指针或引用是一个常见的错误,会导致内存损坏。大多数编译器可以轻松检测到它们。查看您在此处使用 std::move
来消除警告的尝试是错误的。 std::move
只是一种告诉程序员该对象正在从未指定状态移出并保留在未指定状态的方式。你真正想在这里做的是 return 对象的值:
Meal prepareVegMeal() // Notice no reference
{
Meal meal;
//...
return meal;
}
但要小心,因为虚函数调用不适用于值语义。