How to inherit from an abstract class properly in C++? - c++

I couldn't find a proper topic for this question as I haven't got a proper error message.
I'm trying to create a management system for a restaurant which mainly provides pizza as well as other foods(pasta, wings, etc). I want this system to be used by the staff. I have created an abstract class named Foods that can be used to inherit by other foods. So far I have created a class that inherits from Foods named Pizza. Below are my code.
PS: I have used namespaces for organize foods and staff members separately. As far as I know some people doesn't recommend namespace and my apologies if you're one of them.
interfaces.h
#include <vector>
#include <string>
namespace foods{
class Food{
double price;
// since the sauces and drinks are given with foods.
static const std::vector<std::string> sauces;
static const std::vector<std::string> drinks;
public:
virtual int retPrice() = 0;
virtual void ask() = 0; // ask user what to add
virtual ~Food() = default;
};
const std::vector<std::string> Food::sauces = {"Blue cheese", "Garlic", "Honey BBQ", "Marinara"};
const std::vector<std::string> Food::drinks = {"Pepsi", "Mountain Dew", "Coca Cola"};
class Pizza: public Food{
const double price;
const std::string pizzaType; // whether it is chicken, beef, etc.
const std::string size; // small, medium or large
int crust = 1; // how crust it is from 1-5
std::vector<std::string> toppings; // to store toppings
public:
Pizza(): price(15), pizzaType(" "), size(" "){}
int retPrice() override; // the price should change according to the type
void ask() override; // ask the customer for a pizza
void createACustom(); // create a custom pizza with desired toppings
};
};
functions.cpp
#include <iostream>
#include "interfaces.h"
namespace foods{
int Pizza::retPrice(){
return (price+5);
}
void Pizza::ask(){
std::cout << "Hello World!";
}
}
test.cpp
#include "interfaces.h"
int main(){
foods::Pizza* pizza = new foods::Pizza();
}
And I'm getting following error.
/usr/bin/ld: /tmp/ccQRR5B8.o: warning: relocation against `_ZTVN5foods5PizzaE' in read-only section `.text._ZN5foods5PizzaC2Ev[_ZN5foods5PizzaC5Ev]'
/usr/bin/ld: /tmp/ccQRR5B8.o: in function `foods::Pizza::Pizza()':
test.cpp:(.text._ZN5foods5PizzaC2Ev[_ZN5foods5PizzaC5Ev]+0x2b): undefined reference to `vtable for foods::Pizza'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
I tried using the keyword override and also made a default deconstructor, yet nothing seems working. I want to know what this error message means and a solution for this. In addition to that what is vtable?
Appreciate your time and answers.
EDIT 1
I have compiled it with g++ -Wall -Wextra test.cpp functions.cpp -o test, which is wrong and then I did g++ -Wall -Wextra test.cpp functions.cpp -o test and I'm getting following error.
/usr/bin/ld: /tmp/ccmv2G17.o:(.bss+0x0): multiple definition of `foods::Food::sauces[abi:cxx11]'; /tmp/ccuBNQjX.o:(.bss+0x0): first defined here
/usr/bin/ld: /tmp/ccmv2G17.o:(.bss+0x20): multiple definition of `foods::Food::drinks[abi:cxx11]'; /tmp/ccuBNQjX.o:(.bss+0x20): first defined here
collect2: error: ld returned 1 exit status
Why is it saying that it has multiple definitions?

You need to implement the static member variables sauces and drinks in functions.cpp and not in interfaces.h.
functions.cpp
namespace foods {
int Pizza::retPrice() {
return (price + 5);
}
void Pizza::ask() {
std::cout << "Hello World!";
}
// implement the static variables here.
const std::vector<std::string> Food::sauces = { "Blue cheese", "Garlic", "Honey BBQ", "Marinara" };
const std::vector<std::string> Food::drinks = { "Pepsi", "Mountain Dew", "Coca Cola" };
}
And remove them from interfaces.h.
If you implement them in interfaces.h they end up being implemented in each .cpp file that includes interfaces.h.
It's basically the same problem as if you define a global variable in a .h file.

Related

linking hpp and cpp issues with g++ compiler

I am relatively new to c++ and have a solid C and Object Oriented background. So I have the following code written.
I have Person.hpp which is as follows:
class Person
{
private:
struct h
{
char *name;
unsigned char age;
};
struct h entities;
public:
unsigned char age();
char *name();
Person(char *name, unsigned char a);
~Person();
};
The Person.cpp looks as follows:
#include "Person.hpp"
char *Person::name()
{
return entities.name;
}
unsigned char Person::age()
{
return entities.age;
}
Person::Person(char *name, unsigned char a)
{
entities.name = name;
entities.age = a;
}
And finally main.cpp looks like the following:
#include <iostream>
#include "Person.hpp"
int main()
{
Person someone("something", 100);
printf("%s is %d old\n", someone.name(), someone.age());
return 0;
}
I am already aware of the warning that it will cause due to the fact that the string is not constant. This will not cause an error when trying to compile. I tried compiling the code in two different methods. One by just compiling all together which would look like the following:
g++ -o main main.cpp Person.cpp
And the second method I tried was to compile them into object files and then link them together:
g++ -c main.cpp
g++ -c Person.cpp
g++ -o main main.o Person.o
They both give the following error:
/usr/bin/ld: main.o: in function `main':
main.cpp:(.text+0x5b): undefined reference to `Person::~Person()'
/usr/bin/ld: main.cpp:(.text+0x6e): undefined reference to `Person::~Person()'
collect2: error: ld returned 1 exit status
Any help would be greatly appreciated.
You have a destructor declared here in your header file:
public:
unsigned char age();
char *name();
Person(char *name, unsigned char a);
~Person(); // <===== Declared here
But you haven't provided any definitions for it.
However, the destructor is called after your main function returns (you can learn more about this behavior here), so it needs a definition.
You can make the compiler generate the definition for you by either omitting the destructor declaration or use ~Person() = default.
The problem is that you declare both an constructor and destructor in Person.hpp, but only define the constructor in the Person.cpp file.
Try add the following code to Person.cpp:
Person::~Person()
{
}

a typo on page 252 of the book " a complete guide to c++"?

I'm reading the book "a complete guide to c++". I think there is a typo there on page 252. So I have three files as the following.
In file account.h,
// account.h
// Defining the class Account. class definition (methods prototypes) is usually put in the header file
// ---------------------------------------------------
#ifndef _ACCOUNT_ // if _ACCOUNT_ is not defined
#define _ACCOUNT_
#include <iostream>
#include <string>
using namespace std;
class Account
{
private:
string name;
unsigned long nr;
double balance;
public: //Public interface:
bool init( const string&, unsigned long, double);
void display();
};
#endif
// _ACCOUNT_
In file account.cpp,
// account.cpp
// Defines methods init() and display().
// ---------------------------------------------------
#include "account.h" // Class definition
#include <iostream>
#include <iomanip>
using namespace std;
// The method init() copies the given arguments
// into the private members of the class.
bool Account::init(const string& i_name,
unsigned long i_nr,
double i_balance)
{
if( i_name.size() < 1)
return false; // check data format to make sure it is valid
name = i_name;
nr = i_nr;
balance = i_balance;
return true;
}
// the method display() outputs private data.
void Account::display()
{
cout << fixed << setprecision(2)
<< "--------------------------------------\n"
<< "Account holder:" << name << '\n'
<< "Account number:" << nr << '\n'
<< "Account balance:" << balance << '\n'
<< "--------------------------------------\n"
<< endl;
}
And finally, in file account_t.cpp
// account_t.cpp
// Uses objects of class Account.
// ---------------------------------------------------
#include "account.h" // header file which contains class definition; (prototype for member functions)
int main()
{
Account current1, current2; // create two instances with name current1, current2
current1.init("Cheers, Mary", 1234567, -1200.99);
// have to call the init function to initialize a Account object; init function is public; members properties are private;
// that's why can not do current1.name = "nana" outside of the class definition
current1.display();
// current1.balance += 100; // Error: private member
current2 = current1;
current2.display();
current2.init("Jones, Tom", 3512347, 199.40);
current2.display();
Account& mtr = current1; // create a reference, which points to object current1
mtr.display();
return 0;
}
I do not think it's correct; because obviously there is no way to get access the init member methods and the display member methods, right? I hope this is not a naive question.
EDIT: I tried to run the main function in file account_t.cpp, and got the following output.
~$ g++ account_t.cpp
/tmp/ccSWLo5v.o: In function `main':
account_t.cpp:(.text+0x8c): undefined reference to `Account::init(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned long, double)'
account_t.cpp:(.text+0xb6): undefined reference to `Account::display()'
account_t.cpp:(.text+0xd5): undefined reference to `Account::display()'
account_t.cpp:(.text+0x132): undefined reference to `Account::init(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned long, double)'
account_t.cpp:(.text+0x15c): undefined reference to `Account::display()'
account_t.cpp:(.text+0x176): undefined reference to `Account::display()'
collect2: error: ld returned 1 exit status
Any more comments are greatly appreciated.
There is no issue with what you are asking about. init and display are declared public in the definition of class Account and the file with the class definition is #includeed properly in the .cpp using these methods.
In order to use a function only its declaration is needed (which is in the class definition in the header which is included). The definition/implementation in the .cpp file is not needed to use the function.
Each .cpp is compiled individually (called a translation unit) and afterwards each use of a function (for which only the declaration might have been available) is linked to the correct definitions from the other translation units if necessary via the function's scope, name and signature. This is called the linking process.
An introductory book to C++ should explain how compilation and linking process work.
For g++, there is an easy way to compile all .cpp files individually as translation units and then directly link them together:
g++ account_t.cpp account.cpp
You always need to add all .cpp to the compiler invocation like that. (There are alternative ways, but this is the easiest one.)
However, as mentioned in the comments there are other issues with this program:
_ACCOUNT_ is a reserved identifier that one may not #define in a program. All identifiers starting with an underscore followed by a capital letter are reserved. Using them causes undefined behavior.
using namespace std; is bad, at the very least when used in a header file, see Why is "using namespace std;" considered bad practice?.
Classes have constructors. One should not write init methods in most cases. The constructor is responsible for constructing and initializing class instances. But no constructor is used in the code.
Money should never be stored in double, because arithmetic with double is imprecise. You should store the value in an integer type in dimensions of the smallest relevant unit of money (e.g. cents).
#include <iostream> is not needed in the header file. One should avoid adding #includes that are not needed.
init returns a boolean indicating successful initialization. But main never checks that value. If a function can fail with an error value, then you must check that error value returned by the function to make sure that continuing the rest of the program is safe.
Some of these points may be excusable as simplification for a beginner program, depending on how far the book got at this point (though 200+ pages should already cover a lot), but others aren't.
Example of constructor use doing the same thing:
class Account
{
private:
string name;
unsigned long nr;
double balance;
public: //Public interface:
Account(const string&, unsigned long, double);
void display();
};
Account::Account(const string& i_name,
unsigned long i_nr,
double i_balance)
: name(i_name), nr(i_nr), balance(i_balance)
{
}
int main()
{
Account current1("Cheers, Mary", 1234567, -1200.99);
// Create Account instance and initialize private members; constructor is public; members properties are private;
// that's why can not do current1.name = "nana" outside of the class definition
current1.display();
// current1.balance += 100; // Error: private member
Account current2 = current1; // Create second Account instance and copy private members from first one
current2.display();
current2 = Account("Jones, Tom", 3512347, 199.40); // Replace instance with a copy of a new one
current2.display();
Account& mtr = current1; // create a reference, which points to object current1
mtr.display();
return 0;
}
The i_name.size() < 1 check (which is weirdly written, why not i_name.size() == 0?) would be realized by throwing an exception from the constructor:
Account::Account(const string& i_name,
unsigned long i_nr,
double i_balance)
: name(i_name), nr(i_nr), balance(i_balance)
{
if(i_name.size() == 0) {
throw invalid_argument("Account does not accept empty names!");
}
}
This requires #include<stdexcept> and is a more advanced topic.

Undefined reference error when compiling with g++ compiler in cygwin

I'm just starting to work on a project for class and I'm getting an error that I'm unsure how to fix. I'll do my best to provide all necessary details but if you need any more info please let me know. The project is to create a simple hash function and I'm getting a linker error when trying to compile. I will post all of my code as well as the error I receive when compiling. I have taken two previous programming classes but that was quite a while ago and I'm pretty out of practice at the moment so it may be an obvious mistake. Ignore all commented out code in hash.h because that is just function definitions that my teacher provided that I have not yet implemented. She also specifically requested that we put our hashing function in its own separate file. I should also specify that I'm using a Windows machine with cygwin64 to compile my code.
hash.h:
#ifndef __HASH_H
#define __HASH_H
#include <string>
#include <list>
using std::string;
using std::list;
class Hash {
public:
void remove(string); // remove key from hash table
//void print(); // print the entire hash table
void processFile(string); // open file and add keys to hash table
//bool search(string); // search for a key in the hash table
//void output(string); // print entire hash table to a file
//void printStats(); // print statistics
private:
// HASH_TABLE_SIZE should be defined using the -D option for g++
//list<string> hashTable [HASH_TABLE_SIZE];
list<string> hashTable [100];
int collisions;
int longestList;
double avgLength;
int hf(string); // the hash function
void insert(string);
// put additional variables/functions below
// do not change anything above!
};
#endif
hash.cpp:
#include "hash.h"
void Hash::remove(string string_to_remove)
{
int index = hf(string_to_remove);
for(list<string>::iterator iter = hashTable[index].begin(); iter != hashTable[index].end(); iter++)
{
if(*iter == string_to_remove)
hashTable[index].erase(iter);
}
}
void Hash::insert(string string_to_add)
{
int index = hf(string_to_add);
hashTable[index].push_back(string_to_add);
}
void Hash::processFile(string file_name)
{
insert(file_name);
}
hash_function.cpp
#include "hash.h"
int Hash::hf(string input)
{
int sum = 0;
for (int i = 0; i < 5; i++)
sum += (int)input[i];
cout << sum % 100 << endl;
return [sum % 100 /*HASH_TABLE_SIZE*/];
}
Compiler Error:
$ g++ hash_function.cpp testmain.cpp hash.cpp -o test
/tmp/cc42z5XM.o:hash.cpp:(.text+0x32): undefined reference to `Hash::hf(std::string)'
/tmp/cc42z5XM.o:hash.cpp:(.text+0x32): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `Hash::hf(std::string)'
/tmp/cc42z5XM.o:hash.cpp:(.text+0x13c): undefined reference to `Hash::hf(std::string)'
/tmp/cc42z5XM.o:hash.cpp:(.text+0x13c): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `Hash::hf(std::string)'
collect2: error: ld returned 1 exit status

Is the cartTexture member actually initialized correctly?

Problem Description:
I am working on making a Cart class that has a sf::Sprite(cartSprite) in each instance. However, the static sf::Texture(cartTexture) from which carts are generated is shared between all Cart objects. On construction of a Cart, the sprite is loaded from the texture.
According to the documentation, the sf::Texture has a default constructor that:
Creates an empty texture.
However, when I compile the code below, that declares, but does not define, the sf::Texture, I get linker errors:
/tmp/cclJ5hJp.o: In function `Cart::Cart(sf::Vector2<float> const&, sf::Color, float)':
main.cpp:(.text._ZN4CartC2ERKN2sf7Vector2IfEENS0_5ColorEf[_ZN4CartC5ERKN2sf7Vector2IfEENS0_5ColorEf]+0x46): undefined reference to `Cart::cartTexture'
main.cpp:(.text._ZN4CartC2ERKN2sf7Vector2IfEENS0_5ColorEf[_ZN4CartC5ERKN2sf7Vector2IfEENS0_5ColorEf]+0x87): undefined reference to `Cart::cartTexture'
main.cpp:(.text._ZN4CartC2ERKN2sf7Vector2IfEENS0_5ColorEf[_ZN4CartC5ERKN2sf7Vector2IfEENS0_5ColorEf]+0x9b): undefined reference to `Cart::cartTexture'
main.cpp:(.text._ZN4CartC2ERKN2sf7Vector2IfEENS0_5ColorEf[_ZN4CartC5ERKN2sf7Vector2IfEENS0_5ColorEf]+0xb6): undefined reference to `Cart::cartTexture'
collect2: error: ld returned 1 exit status
Does this mean that I should initialize (define) cartTexture? If so, how do I do that at compile-time when the only constructors for sf::Texture are "copy from another sf::Texture" and the one mentioned above? I can't use a function call to load an image into it, right?
Compile the code with: g++ main.cpp -Wall -Wextra -Werror -std=c++11 -lsfml-graphics -lsfml-window -lsfml-system -o exec
Code:
main.cpp:
#include <SFML/Graphics.hpp>
#include <string>
#include <iostream>
#include <mutex>
class Cart : public sf::Drawable {
public:
// Constructor- create with position, orientation, and color.
Cart(const sf::Vector2f& cartPos_, sf::Color cartColor_, float cartAngle_) {
if (!cartTexture.loadFromFile(textureLoc)) {
std::cout << "File is nonexistent." << std::endl;
} else {
cartTexture.setSmooth(true);
cartTexture.setRepeated(false);
}
cartSprite.setTexture(cartTexture);
cartSprite.setPosition(cartPos_);
cartSprite.setRotation(cartAngle_);
cartSprite.setColor(cartColor_);
}
private:
void draw(sf::RenderTarget& target, sf::RenderStates states) const {
target.draw(cartSprite, states);
return;
}
static sf::Texture cartTexture;
static const std::string textureLoc;
sf::Sprite cartSprite;
};
const std::string Cart::textureLoc = "cart-empty.png";
int main() {
Cart testCart(sf::Vector2f(), sf::Color(200,200,200), 0);
return 0;
}
static class member variables have to be initialized/defined outside of the class definition. You do this for textureLoc but not cartTexture. Before main() add:
sf::Texture Cart::cartTexture;

Undefined reference to constructor

I'm a Java developer experimenting with C++.
I just created a new class. In my other class I want to have list where I can store Filter objects.
Filter.h
#ifndef FILTER_H_
#define FILTER_H_
class Filter {
public:
Filter(int id);
int id;
~Filter();
};
#endif /* FILTER_H_ */
Filter.cpp
#include "Filter.h"
Filter::Filter(int id) {
this.id = id;
}
Filter::~Filter() {
}
Cars.h
#include "Filter.h"
...
...
private:
std::vector<Filter> filters;
Cars.cpp
so in a function here I try to do this:
int id = 2;
Filter *filter = new Filter(id);
which generate this error:
Cars.cpp:120: undefined reference to `Filter::Filter(int)'
stl_construct.h:83: undefined reference to `Filter::~Filter()'
What's the reason for this?
The error is generated by the linker because it can not see where the definition of the constructor is located.
If you are using an IDE, you should add both .cpp files to the project so that they can be compiled together and the definition would be found by the linker.
If not, then you have to combine them yourself -assuming you are using gcc:
g++ cars.cpp filter.cpp
will combine them into one executable and should not show you that error