简体   繁体   English

虚函数 C++ 的链接器错误

[英]Linker error with virtual functions c++

I am not sure what is wrong with this code, I am learning Builder pattern .我不确定这段代码有什么问题,我正在学习Builder pattern The example code is in Java and I am trying to code the same in C++, butI am getting linker errors.示例代码使用 Java 编写,我尝试使用 C++ 编写相同的代码,但出现链接器错误。 I searched and read about it all and still couldn't find the right way and hence, posting it here.我搜索并阅读了所有内容,但仍然找不到正确的方法,因此将其发布在这里。 If I am missing something really trivial, my apologies.如果我遗漏了一些非常微不足道的东西,我深表歉意。

#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;
}

Following the comments, here is the error I used to get prior to adding the definition of the Item class:在评论之后,这是我在添加 Item 类的定义之前遇到的错误:

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 ==========

After adding the definitions, I get the following output:添加定义后,我得到以下输出:

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

But the required output is:但所需的输出是:

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

I am not sure how to change my code to get the required output.我不确定如何更改我的代码以获得所需的输出。 Please help.请帮忙。

Thanks a lot, in advance.非常感谢,提前。

The error is in these lines:错误在这些行中:

class MealBuilder
{
public:
    Meal prepareVegMeal()  <-- changed this line, removed reference
    {
        ....
    }

    Meal prepareNonVegMeal()  <-- changed this line, removed reference
    {
       ....
    }
};

Once you fix this, you will need member functions in Item class.解决此问题后,您将需要 Item 类中的成员函数。

Reason: In C++, a non-const reference cannot bind to a temporary object.原因:在 C++ 中,非常量引用不能绑定到临时对象。 C++ doesn't want you to accidentally modify temporaries. C++ 不希望您不小心修改临时文件。 You are returning using std::move.您正在使用 std::move 返回。 std::move is a cast to an rvalue reference. std::move 是对右值引用的强制转换。 By convention, rvalue references are treated as "references you are permitted to move the data out of, as the caller promises they really don't need that data anymore".按照惯例,右值引用被视为“允许将数据移出的引用,因为调用者承诺他们真的不再需要这些数据了”。

Check this answer for more details.检查此答案以获取更多详细信息。

How come a non-const reference cannot bind to a temporary object? 为什么非常量引用不能绑定到临时对象?

Your Item class member functions are declared but never defined.您的Item类成员函数已声明但从未定义。 You might get away with it if you never get to instantiate this class or classes derived from it.如果您永远无法实例化此类或从它派生的类,您可能会侥幸逃脱。 However this should not be relied upon.然而,这不应该被依赖。

Virtual member function can be either a normal function or pure virtual.虚成员函数可以是普通函数或纯虚函数。

class A {
  virtual void func1() {};
  virtual void func2() = 0;
}

Classes that have at least one pure virtual function can not be instantiated but one can use pointers and references to it.不能实例化至少具有一个纯虚函数的类,但可以使用指针和对它的引用。

Virtual function that declare must be defined.必须定义声明的虚函数。

class B {
  virtual func1();
}

int main() {
  new B;
}

This code would produce linkage error similar to ones you get.这段代码会产生类似于你得到的链接错误。

Though you might not explicitly instantiate the class you implicitly instantiate it once instantiating a class derived from it:虽然您可能不会显式实例化该类,但一旦实例化从它派生的类,您就会隐式实例化它:

class B {
  virtual func1();
}

class C : B {
  virtual func2() {}
}

int main() {
  new C;
}

This code would produce similar linking error.此代码会产生类似的链接错误。

Going back to your case.回到你的案子。 Make sure that all functions are either pure virtual or properly defined.确保所有函数都是纯虚拟的或正确定义的。

There's another error you have in your code.您的代码中还有另一个错误。 It is surprising that compiler lets it go through.令人惊讶的是编译器让它通过。

In functions在函数中

Meal& prepareVegMeal()
{
    Meal meal;
    //...
    return std::move(meal);
}

You are creating a temporary object and returning it by a reference.您正在创建一个临时对象并通过引用返回它。 This temporary object is automatically destroyed upon scope end.这个临时对象在作用域结束时自动销毁。 Returning a pointer or a reference to a temporary object is a common mistake and leads to memory corruption.返回对临时对象的指针或引用是一个常见的错误,会导致内存损坏。 Most compilers can easily detect them.大多数编译器都可以轻松检测到它们。 Looking at your attempt to use std::move here to silence the warning is wrong.看看您在此处使用std::move来消除警告的尝试是错误的。 std::move is just a way to tell programmer that this object is being moved from and left in unspecified state. std::move只是告诉程序员此对象正在从未指定状态移出和离开的一种方式。 What you really want to do here is to return the object by its value:你真正想要做的是通过它的值返回对象:

Meal prepareVegMeal() // Notice no reference
{
    Meal meal;
    //...
    return meal;
}

But be careful because virtual function calls do not work with value semantics.但要小心,因为虚函数调用不适用于值语义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM