2017-12-25 21 views
-2

このコードで何が問題なのかよく分かりませんが、Builder patternを学習しています。サンプルコードはJavaであり、C++で同じコードをコーディングしようとしていますが、リンカエラーが発生しています。私はそれをすべて検索して読んだが、それでも正しい方法を見つけることができず、ここにそれを掲示した。私が本当に些細なことを見逃している場合、私の謝罪。仮想関数のリンカエラー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)" ([email protected]@@UAE?AV? 
[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@XZ) 
1>MealBuilder.obj : error LNK2001: unresolved external symbol "public: 
virtual class Packing * __thiscall Item::packing(void)" (? 
[email protected]@@[email protected]@XZ) 
1>MealBuilder.obj : error LNK2001: unresolved external symbol "public: 
virtual float __thiscall Item::price(void)" ([email protected]@@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 

私はコードを変更して必要な出力を得る方法がわかりません。助けてください。

ありがとうございます。

+6

質問にもリンカエラーをコピーしてみませんか?しかし、まずリンカエラーを引き起こしながら、おそらくプログラムをもっと短くすることができます。 –

+0

入手したエラーコードをお知らせください。 – teivaz

+0

そしてbtw。 'Item'クラスのメンバ関数の定義はどこですか? – teivaz

答えて

0

あなたItemクラスのメンバ関数が宣言されていますが定義されることはありません。このクラスまたはそのクラスから派生したクラスをインスタンス化することが決してできない場合は、それを取り除くことができます。しかし、これは信頼されるべきではありません。

仮想メンバー機能は、通常の機能または純粋な仮想のいずれかになります。

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

少なくとも1つの純粋仮想関数を持つクラスは、インスタンス化することはできませんが、ポインタとその参照を使用できます。

定義する仮想関数を定義する必要があります。

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は、このオブジェクトが指定されていない状態から移動され、残っていることをプログラマに知らせる方法です。実際にここでやりたいことは、その値でオブジェクトを返すことです:

しかし、仮想関数呼び出しは値の意味論では機能しないので注意してください。

0

エラーがこれらの行にあります。

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

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

あなたはこれを修正したら、あなたはItemクラスにメンバ関数が必要になります。

理由: C++では、非const参照は一時オブジェクトにバインドできません。 C++では、一時的に誤って変更することは望ましくありません。あなたはstd :: moveを使って戻ります。 std :: moveはrvalue参照へのキャストです。通常、参照値の参照は、呼び出し側が実際にはデータを必要としないと約束しているため、データを移動することが許可されている参照として扱われます。

詳細については、この回答を確認してください。

How come a non-const reference cannot bind to a temporary object?

+0

これをTeivazの答えに従って変更しました。ありがとうございました。 – Esash