I'm reading a book on C++ and was writing out some code to practice using the interface and implementation of a class. I've searched for solutions to my issue for a while to no avail.
I have a class with an enumeration inside of it. While trying to instantiate an object of that class, I am having trouble accessing the enum types outside of the class. I have tried using Book::Horror, Biblo::Horror, Biblo::Book::Horror, Horror, and even things like Biblo::Book::Genre::Horror. Can't seem to get it to let me access the types of the enum for the instantiation of my object in the main.cpp file.
Any help is appreciated! The more complex uses of C++ are still new to me. Here is my source:
book.h
#include <iostream>
#include <string>
using namespace std;
namespace Biblo{
class Book{
public:
enum Genre{
No_Genre, Horror, Comedy, Romance, Mystery
};
// The rest of this header is working fine I think, just this enum
class Invalid{}; // Used for throwing errors
Book(int n_ISBN, int n_copyrightYear, string n_title, string n_author, Genre n_genre);
Book();
// Accessors (non-modifying)
int getISBN() const { return ISBN; }
int getCopyrightYear() const { return copyrightYear; }
string getTitle() const { return title; }
string getAuthor() const { return author; }
string getGenre() const;
// Mutators
void changeAuthor(string newAuthor);
private:
int ISBN;
int copyrightYear;
string title;
string author;
Genre genre;
}; // End Book
// Helper Functions go here
bool operator==(const Book& a, const Book& b);
bool operator!=(const Book& a, const Book& b);
} // End Biblo
and main.cpp
#include <iostream>
#include "book.h"
using namespace std;
int main()
{
Biblo::Book book(100, 2012, "The Walrus", "The Eggman", Book::Horror); // THIS LINE GIVES ERROR
cout << "ISBN: " << book.getISBN() << endl;
cout << "Copyright: " << book.getCopyrightYear() << endl;
cout << "Title: " << book.getTitle() << endl;
cout << "Author: " << book.getAuthor() << endl;
cout << "Genre: " << book.getGenre() << endl;
return 0;
}
Edit: here is the book.cpp file
#include <iostream>
#include "book.h"
#include <string>
namespace Biblo{
Book::Book(int n_ISBN, int n_copyrightYear, string n_title, string n_author, Genre n_genre)
:ISBN(n_ISBN), copyrightYear(n_copyrightYear), title(n_title), author(n_author), genre(n_genre)
{
// constructor
}
Book::Book()
:ISBN(0), copyrightYear(0), title(""), author(""), genre(Genre::No_Genre)
{
// Default constructor
}
// Accessors
string Book::getGenre() const
{
if (Book.genre == Genre::No_Genre)
return "No Genre!";
if (Book.genre == Genre::Horror)
return "Horror";
if (Book.genre == Genre::Comedy)
return "Comedy";
if (Book.genre == Genre::Romance)
return "Romance";
if (Book.genre == Genre::Mystery)
return "Mystery";
}
// Mutators
void Book::changeAuthor(string newAuthor)
{
author = newAuthor;
}
// Helper Functions
bool operator==(const Book& a, const Book& b)
{
if (a.getISBN() != b.getISBN())
return false;
if (a.getCopyrightYear() != b.getCopyrightYear())
return false;
if (a.getTitle() != b.getTitle())
return false;
if (a.getAuthor() != b.getAuthor())
return false;
if (a.getGenre() != b.getGenre())
return false;
return true;
}
bool operator!=(const Book& a, const Book& b)
{
return !(a==b);
}
} // End Biblo
It seems you tried everything but the thing you needed! The enum is nested inside the Book class which is within the Biblo namespace. The code you are looking for is:
int main()
{
Biblo::Book book(100, 2012, "The Walrus", "The Eggman", Biblo::Book::Horror);
return 0;
}
You need to include the enum class. eg.:
Biblio::Book::Genre::Horror
Bunch of things that are going wrong really.
As others mentioned your enum is stashed one level deeper than you think it is, and your complaint about fixing it then producing an undefined reference is probably because at that point you bump into the fact there's nothing much initialized, and if you got past that you are returning items somewhat poorly when it comes to the enumerator.
If you use the right name space, quickly put in an actual implementation for the constructor, and get the most immediate return for your enum (an int might work) it should work, and probably look like this:
#include <iostream>
#include <string>
using namespace std;
namespace Biblo{
class Book{
public:
enum Genre{
No_Genre, Horror, Comedy, Romance, Mystery
};
// The rest of this header is working fine I think, just this enum
class Invalid{}; // Used for throwing errors
Book(int n_ISBN, int n_copyrightYear, string n_title, string n_author, Genre n_genre);
Book();
// Accessors (non-modifying)
int getISBN() const { return ISBN; }
int getCopyrightYear() const { return copyrightYear; }
string getTitle() const { return title; }
string getAuthor() const { return author; }
int getGenre() const { return genre; }
// Mutators
void changeAuthor(string newAuthor);
private:
int ISBN;
int copyrightYear;
string title;
string author;
Genre genre;
}; // End Book
Book::Book(int n_ISBN, int n_copyrightYear, string n_title, string n_author, Genre n_genre){
ISBN = n_ISBN; copyrightYear = n_copyrightYear; title = n_title; author = n_author;
};
}
using namespace std;
int main()
{
Biblo::Book book(100, 2012, "The Walrus", "The Eggman", Biblo::Book::Horror); // THIS LINE GIVES ERROR
cout << "ISBN: " << book.getISBN() << endl;
cout << "Copyright: " << book.getCopyrightYear() << endl;
cout << "Title: " << book.getTitle() << endl;
cout << "Author: " << book.getAuthor() << endl;
cout << "Genre: " << book.getGenre() << endl;
return 0;
}
Are you sue the book you're following isn't discussing details such as initializer lists or something else for constructors concurrent to, or previous to, the subjects you're looking at?
The code looked somewhat incomplete.
Edited in line here on SO, so bear with the poor formatting and the merged h/cpp look :)
Related
I am tasked to create a Print function that prints user inputted data that is specific to an object. This print function must use the Get() Function commands I created.
I have googled and looked for similar questions but could not find a way of how I could approach this. How can I create this function my teacher wants?
The object I want to print specifically is book1
My code:
#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
class Book {
public:
void SetTitle(string title_input);
string GetTitle();
void SetAuthor(string& author_input);
string GetAuthor();
void SetCopyRightYear(int copyright_year_input);
int GetCopyRightYear();
void PrintBook();
private:
string title;
string author;
int copyright_year;
};
void Book::SetTitle(string title_input) {
title = title_input;
}
string Book::GetTitle() {
return title;
}
void Book::SetAuthor(string& author_input) {
author = author_input;
}
string Book::GetAuthor() {
return author;
}
void Book::SetCopyRightYear(int copyright_year_input) {
copyright_year = copyright_year_input;
}
int Book::GetCopyRightYear() {
return copyright_year;
}
void Book::PrintBook() {
cout << "Title of Book: " << GetTitle() << endl;
cout << "Author of Book: " << GetAuthor() << endl; // Function is broken FIXME
cout << "Copyright Year: " << GetCopyRightYear() << endl;
}
int main ()
{
string title_input = "";
string author_input = "";
int copyright_year_input = 0;
Book book1;
Book book2;
Book book3;
Book book4;
cout << "Enter the book title: ";
cin >> title_input;
book1.SetTitle(title_input);
cout << book1.GetTitle();
cout << "Enter the author name: ";
cin >> author_input;
book1.SetAuthor(author_input);
cout << "Enter the copyright year: ";
cin >> copyright_year_input;
book1.SetCopyRightYear(copyright_year_input);
cout << PrintBook();
Book.h
#pragma once
#include <string>
class Book
{
public:
Book() = default;
~Book() = default;
const std::string GetTitle() const;
const std::string GetAuthor() const;
const int GetCopyRightYear() const;
void SetTitle(const std::string);
void SetAuthor(const std::string);
void SetCopyRightYear(const int);
void PrintBook();
private:
std::string title;
std::string author;
int copyright_year;
};
Book.cpp
#include "Book.h"
// ------------------------------
#include <iostream>
void Book::SetTitle(const std::string title_input)
{
title = title_input;
}
const std::string Book::GetTitle() const
{
return title;
}
const int Book::GetCopyRightYear() const
{
return copyright_year;
}
const std::string Book::GetAuthor() const
{
return author;
}
void Book::SetCopyRightYear(const int copyright_year_input)
{
copyright_year = copyright_year_input;
}
void Book::SetAuthor(const std::string author_input)
{
author = author_input;
}
void Book::PrintBook()
{
std::string output_str = "";
std::cout << "Title of Book: " << GetTitle() << std::endl;
std::cout << "Author of Book: " << GetAuthor() << std::endl;
std::cout << "Copyright Year: " << GetCopyRightYear() << std::endl;
}
main.cpp
// C++ Libraries.
#include <iostream>
#include <string>
// User classes
#include "Book.h"
// Namespaces
int main()
{
std::string title_input = "";
std::string author_input = "";
int copyright_year_input = 0;
// research dynamic memory allocation.
Book book1;
Book book2;
Book book3;
Book book4;
// user sets book title.
std::cout << "Enter the book title: ";
std::getline(std::cin, title_input);
book1.SetTitle(title_input);
// user sets the authors name
std::cout << "Enter the author name: ";
std::getline(std::cin, author_input);
book1.SetAuthor(author_input);
// user inputs the copyright year.
std::cout << "Enter the copyright year: ";
std::cin >> copyright_year_input;
book1.SetCopyRightYear(copyright_year_input);
// Display the information.
book1.PrintBook();
}
Notes:
When you start using multiple namespaces its easier to see what is what if you dont predefine them.
Const correctness means you and other developers know what can be changed and what cant. It also makes things clearer for the compiler.
std::getline reads the whole line including the blank spaces.
Just a quick note on clarity and understanding. At the moment your code is messy which makes it incredibly hard to debug not only for yourself but for others.
I can't tell on here but just in case, your classes should be in header and source code formatting, with a main source code file for the main function (entry point). Whether or not you've been told this information before I would highly recommend doing some research into basic C++. Just for starters I've put some links below to help. Once your code is neatly formatted you might work out what the problem is.
Happy coding :)
References:
Herb Sutter Cpp Convention 2014 - Simplicity over Complexity:
https://www.youtube.com/watch?v=xnqTKD8uD64
Headers and Includes - C++ formatting:
http://www.cplusplus.com/forum/articles/10627/
Also see the tutorials on cplusplus.com.
I've created my code in Microsoft Visual Studio Express 2013 and it compiles and runs fine. I've moved this over to Linux and it gives me a different result for the GPA output. The GPA's are coming as 0 and 6.95281e-310 instead of the 3.9 and 3.5.
Also wondering if there is a difference between the strcmp and strncpy in Linux since I had to add #include <cstring> in my student.h?
Is there something else I should be using instead of strncpy in Linux?
student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <iostream>
#include <cstring>
using namespace std;
class Student
{
public:
Student(const char initId[], double gpa);
bool isLessThanByID(const Student& aStudent) const;
bool isLessThanByGpa(const Student& aStudent) const;
void print()const;
private:
const static int MAX_CHAR = 100;
char id[MAX_CHAR];
double gpa;
};
#endif
student.cpp
#include "student.h"
//implement the required 3 functions here
Student::Student(const char initId[], double gpa) : gpa(gpa)
{
// initialize a newly created student object with the passed in value
strncpy(id, initId, Student::MAX_CHAR - 1);
if (Student::MAX_CHAR > 0)
{
id[Student::MAX_CHAR - 1] = '\0';
}
}
bool Student::isLessThanByID(const Student& aStudent) const
{
// compare the current student object with the passed in one by id.
if (strcmp(id, aStudent.id) > 0)
{
return true;
}
else
{
return false;
}
}
bool Student::isLessThanByGpa(const Student& aStudent) const
{
// compare the current student object with the passed in one by gpa
if (gpa < aStudent.gpa)
{
return true;
}
else
{
return false;
}
}
void Student::print() const
{
cout << id << '\t' << gpa << endl;
}
app.cpp
#include "student.h"
int main()
{
Student s1("G10", 3.9);
Student s2("G20", 3.5);
s1.print();
s2.print();
if(s1.isLessThanByID(s2))
{
cout << "about right!" << endl;
}
else
{
cout << "uhmm ..." << endl;
}
if(!s1.isLessThanByGpa(s2))
{
cout << "about right!" << endl;
}
else
{
cout << "uhmm ..." << endl;
}
//system("pause");
return 0;
}
strcmp compares strings http://www.cplusplus.com/reference/cstring/strcmp/
strncpy copies them http://www.cplusplus.com/reference/cstring/strncpy/
There IS a great diff really between them, in Linux and other systems as well.
Not sure where I am at the moment, trying to figure it out. I need to initialize the members in print() const as it is giving me random gibberish. No matter what I try to do, it does not seem to work. Not sure what to even do. Can anyone give me a hand?
*edit: Added in the rest of the code. Forgot it when I submitted the first time.
Student.cpp
#include "student.h"
//implement the required 3 functions here
Student::Student(const char initId[], double gpa)
{
// initialize a newly created student object with the passed in value
}
bool Student::isLessThanByID(const Student& aStudent) const
{
// compare the current student object with the passed in one by id.
if (strcmp(id, aStudent.id) > 0)
{
return true;
}
else
{
return false;
}
}
bool Student::isLessThanByGpa(const Student& aStudent) const
{
// compare the current student object with the passed in one by gpa
if (gpa < aStudent.gpa)
{
return true;
}
else
{
return false;
}
}
void Student::print() const
{
cout << id << '\t' << gpa << endl;
}
student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <iostream>
using namespace std;
class Student
{
public:
Student(const char initId[], double gpa);
bool isLessThanByID(const Student& aStudent) const;
bool isLessThanByGpa(const Student& aStudent) const;
void print()const;
private:
const static int MAX_CHAR = 100;
char id[MAX_CHAR];
double gpa;
};
#endif
app.cpp
#include "student.h"
int main()
{
Student s1("G10", 3.9);
Student s2("G20", 3.5);
s1.print();
s2.print();
if(s1.isLessThanByID(s2))
{
cout << "about right!" << endl;
}
else
{
cout << "uhmm ..." << endl;
}
if(!s1.isLessThanByGpa(s2))
{
cout << "about right!" << endl;
}
else
{
cout << "uhmm ..." << endl;
}
system("pause");
return 0;
}
There is nothing in that code that sets the values of Student::id and Student::gpa. Your constructor has parameters initId and gpa; you should copy those into your object. Based on the declaration of Student that you provided, something this should be appropriate:
Student::Student(const char initId[], double gpa) : gpa(gpa)
{
strncpy(id, initId, Student::MAX_CHAR-1);
id[Student::MAX_CHAR-1] = '\0';
}
This is my class:
using namespace std;
Class Book {
public:
Book();
Book(vector<string>*, string, int);
Book(const Book&);
~Book();
Book& operator=(const Book&);
void update(vector<string>*);
void update(string);
void update(int);
int getYear() const{
return year;
};
string getTitle() const{
return title;
};
bool operator==(const Book&);
bool operator!=(const Book&);
friend std::ostream& operator<<(std::ostream&, const Book&);
void getAuthors();
private:
vector<string>* authors;
string title;
int year;
};
#endif /* BOOK_H */
Here is the source for it:
#include "Book.h"
using namespace std;
Book::Book():year(0), title(NULL), authors(NULL){}
Book::Book(vector<string>* bookauthors,string booktitle, int bookyear ){
authors = bookauthors;
title = booktitle;
year = bookyear;
}
Book::Book(const Book& aBook){
authors = aBook.authors;
title = aBook.title;
year = aBook.year;
}
Book::~Book(){
delete authors;
delete &title;
delete &year;
}
bool Book::operator==(const Book &aBook){
if(getYear() == aBook.getYear() && getTitle() == aBook.getTitle())
return true;
else return false;
}
bool Book::operator != (const Book &aBook){
if(getYear() != aBook.getYear() && getTitle() != aBook.getTitle())
return true;
else return false;
}
Book& Book::operator =(const Book& rhs){
if(this != &rhs){
authors = rhs.authors;
title = rhs.title;
year = rhs.year;
}
return *this;
}
void Book::update(int newyear){
year = newyear;
}
void Book::update(string newtitle){
title = newtitle;
}
void Book::update(vector<string>* newauthors){
authors = newauthors;
}
std::ostream& operator <<(std::ostream& os, const Book& b){
os<<b.getTitle()<<", "<<b.getYear();
return os;
}
Here is the main file where it runs:
#include "Book.h"
#include <iostream>
#include <limits.h>
//This is the test funcion posted on the class website
using namespace std;
int main(){
//testing constructor
vector<string> authors;
authors.push_back("Ritchie");
authors.push_back("Kernighan");
Book a(&authors, "C", 1990);
authors.push_back("Whatever");
cout << "Book a is: " << a << endl;
cout << "Expected: (C, 1990, Ritchie & Kernighan)" << endl;
//testing copy constructor
Book b(a);
a.update(&authors);
cout << "Book b is: " << b << endl;
cout << "Expected: (C, 1990, Ritchie & Kernighan)" << endl;
//testing constructor
vector<string> authors2;
authors2.push_back("Crockford");
Book c(&authors2, "JavaScript", 2008);
cout << "Book c is: " << c << endl;
cout << "Expected: (JavaScipt, 2008, Crockford)" << endl;
//testing assignment operator
authors2.push_back("whatever");
a=c;
cout << "Book a is changed to: " << a << endl;
cout << "Expected: (JavaScipt, 2008, Crockford)" << endl;
for(int i=0; i < 200000000; i++)
b=c;
cout << "Book b is changed to: " << b << endl;
cout << "Expected: (JavaScipt, 2008, Crockford)" << endl;
}
I keep getting this when I run it:
bookclass(58316) malloc: * error for object 0x7fff522d78b0: pointer being freed was not allocated* set a breakpoint in malloc_error_break to debug
I'm new to C++ so I'm not sure how to allocate the memory. I tried using malloc and it didn't work.
The members are located inside the object, i.e., the memory for them is allocated with the Book object and neither can nor need to delete the memory explicitly. Basically, you need to match your explicit allocations using new with calls to delete but you never need to release something which isn't allocated somewhere explicitly.
That is, you get the error when you try to delete title or delete year. It may also happen when trying to delete authors depending on where authors is coming from. In general, you don't want to delete objects you haven't allocated. Your Book class possibly unreasonably takes ownership of the authors vector.
This is probably a good time to learn to use Valgrind, which will give you much richer debugging facilities for this kind of error.
In your destructor, you are destroying title and year via a pointer. You do not actually need to do this, as they are allocated statically (i.e. you didn't create them using new), so it's complaining that you are trying to delete something you did not create dynamically.
In addition, you are deleting your std::vector, which can refer to the same std::vector contained within another class. Since it's possible you have two classes containing the same reference, you need to find a smarter way to delete this so you don't invoke a double free.
#include <iostream>
#include <string>
using namespace std;
class BookData
{
string Title;
int Qty;
public:
void setTitle(string in_title) { Title = in_title;}
string setQty(int in_qty) { Qty = in_qty; }
string getTitle() { return Title; }
int getQty() { return Qty; }
};
int main()
{
BookData book;
book.setTitle("Starting Out with C++");
book.setQty(10);
cout << "Title is " << book.getTitle() << ".\n\n";
cout << "Quantity is " << book.getQty() << ".\n\n";
return 0;
}
When I compile all I get is an empty console. Any suggestions?
Change the return type of BookData::setQty() from string to void.
Without this change, it should still work fine. On my Linux machine, it crashes when setQty() is called with a return type of string and no string being returned.
Does the console close immediately? If so, put this before return 0;:
System("PAUSE");
Or:
std::cin.ignore();
Or (for MSVC++2010):
int temp;
std::cin >> temp;