2017-12-25 2 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; 
} 

적어도 하나의 순수 가상 기능이있는 클래스는 인스턴스화 할 수 없지만 포인터와 참조를 사용할 수 있습니다.

선언 할 가상 함수를 정의해야합니다.

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

하지만 조심해야 해.

0

오류는 다음 줄에 : 당신은이 문제를 해결 한 후에는 항목 클래스의 멤버 함수가 필요합니다

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

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

.

이유 : C++에서 비 const 참조는 임시 개체에 바인딩 할 수 없습니다. C++은 일시적으로 실수로 수정하기를 원하지 않습니다. 당신은 std :: move를 사용하여 복귀하고 있습니다. std :: move는 rvalue 참조에 캐스트입니다. 관습 적으로 rvalue 참조는 "호출자가 실제로 더 이상 데이터를 필요로하지 않는다고 약속 했으므로 데이터를 이동할 수있는 참조"로 처리됩니다.

자세한 내용은이 답변을 확인하십시오.

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

+0

Teivaz의 답변에 따라 이것을 변경했습니다. 고맙습니다. – Esash