簡體   English   中英

虛函數 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;
}

在評論之后,這是我在添加 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 ==========

添加定義后,我得到以下輸出:

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
    {
       ....
    }
};

解決此問題后,您將需要 Item 類中的成員函數。

原因:在 C++ 中,非常量引用不能綁定到臨時對象。 C++ 不希望您不小心修改臨時文件。 您正在使用 std::move 返回。 std::move 是對右值引用的強制轉換。 按照慣例,右值引用被視為“允許將數據移出的引用,因為調用者承諾他們真的不再需要這些數據了”。

檢查此答案以獲取更多詳細信息。

為什么非常量引用不能綁定到臨時對象?

您的Item類成員函數已聲明但從未定義。 如果您永遠無法實例化此類或從它派生的類,您可能會僥幸逃脫。 然而,這不應該被依賴。

虛成員函數可以是普通函數或純虛函數。

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

不能實例化至少具有一個純虛函數的類,但可以使用指針和對它的引用。

必須定義聲明的虛函數。

class B {
  virtual func1();
}

int main() {
  new B;
}

這段代碼會產生類似於你得到的鏈接錯誤。

雖然您可能不會顯式實例化該類,但一旦實例化從它派生的類,您就會隱式實例化它:

class B {
  virtual func1();
}

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

int main() {
  new C;
}

此代碼會產生類似的鏈接錯誤。

回到你的案子。 確保所有函數都是純虛擬的或正確定義的。

您的代碼中還有另一個錯誤。 令人驚訝的是編譯器讓它通過。

在函數中

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

您正在創建一個臨時對象並通過引用返回它。 這個臨時對象在作用域結束時自動銷毀。 返回對臨時對象的指針或引用是一個常見的錯誤,會導致內存損壞。 大多數編譯器都可以輕松檢測到它們。 看看您在此處使用std::move來消除警告的嘗試是錯誤的。 std::move只是告訴程序員此對象正在從未指定狀態移出和離開的一種方式。 你真正想要做的是通過它的值返回對象:

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

但要小心,因為虛函數調用不適用於值語義。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM