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.
Related
I'm new to the world of C++ (and OOP).
I'm learning about classes and pointers at this moment, but I'm stuck and hope someone can explain to me what I'm missing or should dig deeper into to broaden my understanding.
Example 1 works:
#include <iostream>
#include <string.h>
using namespace std;
class MyClass
{
private:
char* age;
public:
MyClass(const char* initData)
{
age = NULL;
cout << "In default constructor working on pointers" << endl;
age = new char [strlen(initData)+1];
strcpy(age,initData);
//age = initData;
}
~MyClass()
{
cout << "In destructor working on pointers" << endl;
delete [] age;
}
const char* GetAge()
{
return age;
}
};
int main()
{
//MyClass firstClass(10);
//cout << "First attempt: " ;
//cout << firstClass.GetAge() << endl;
MyClass secondClass("A test from B");
cout << "Second attempt: ";
cout << secondClass.GetAge() << endl;
return 0;
}
However This does not work:
#include <iostream>
#include <string.h>
using namespace std;
class MyClass
{
private:
int* age;
public:
MyClass(const int* initData)
{
cout << "In default constructor working on pointers" << endl;
age = new int;
//strcpy(age,initData);
age = initData;
}
~MyClass()
{
cout << "In destructor working on pointers" << endl;
delete age;
}
const int* GetAge()
{
return age;
}
};
int main()
{
MyClass firstClass(10);
cout << "First attempt: " ;
cout << firstClass.GetAge() << endl;
//MyClass secondClass("B");
//cout << "Second attempt: ";
//cout << secondClass.GetAge() << endl;
return 0;
}
I feel that it has to do with the fact that I pass an int to a constant pointer.
And then try to assign a constant from r-value to l-value with the = operator.
Although it think this is permitted, since I say 'const int* initData' and this tells the compiler to keep the data from changing but memory address can change?
So in my understanding, I pass value 10 to the class that makes a pointer in the default constructor, this makes a memory address and stores it in initData.
I then pass the r-value InitData (mem address) to the l-value age pointer, that accepts memory addresses.
If I try with the exact same code, but use char and copy data over in my first example it works...
Can anyone explain to me what I'm missing, Thanks in advance!
Edit:
I Think I found my solution and understand it better.
Could anyone verify that this is correct, or which is the (more) correct form:
Scenario 1:
#include <iostream>
using namespace std;
class MyClass
{
private:
int* age;
public:
MyClass(const int *initData)
{
cout << "In default constructor working on pointers" << endl;
age = new int;
(*age) = (*initData);
}
~MyClass()
{
cout << "In destructor working on pointers" << endl;
delete age;
}
const int* GetAge()
{
return age;
}
};
int main()
{
int aNum = 10;
MyClass firstClass(&aNum);
cout << "First attempt: " ;
cout << *firstClass.GetAge() << endl;
return 0;
}
Or Scenario 2:
#include <iostream>
using namespace std;
class MyClass
{
private:
int* age;
public:
MyClass(const int &initData)
{
cout << "In default constructor working on pointers" << endl;
age = new int;
(*age) = (initData);
}
~MyClass()
{
cout << "In destructor working on pointers" << endl;
delete age;
}
const int* GetAge()
{
return age;
}
};
int main()
{
MyClass firstClass(10);
cout << "First attempt: " ;
cout << *firstClass.GetAge() << endl;
return 0;
}
I Would think solution 2, as that uses reference to memory and thus speeds up the execution?
Thanks for the assistance already, and sorry for the long post...
First: you can not set a const pointer to a non-const pointer, you can circumvent this using a const_cast<int*>(initData) which gets rid of the const-ness of initData when assigning to age.
But i do not think this is what you want to achieve.
So first of all a pointer is just pointing to memory - that memory must therefore be managed by someone else than the pointer. Usually you only need a pointer if you want to reference something or iterate - if you intend to be the owner of that memory you might not want to use a pointer.
Secondly: if you start learning C++ now, try to learnt 'modern' C++ and utilize smart_pointers (see smart pointers form cppreference).
A rule of thumb: if you do not necessarily need a pointer (no need for iteration, direct memory access) use by reference instead.
But bottom line: your example looks like it is not meant to be used with pointers at all. Just store the int value.
class MyClass
{
private:
int age;
public:
// pass by const reference, this is cheap and good practice
MyClass(const int& initData)
{
cout << "In default constructor working on pointers" << endl;
// here the value of initData is actually copied into age
age = initData;
}
And if you really want to try pointers: start with the std::shared_ptr which is a ref-counting pointer easy to use.
Example:
#include <memory>
#include <iostream>
class test
{
private:
std::shared_ptr<int> age;
public:
// cheap and quick const ref
test(const std::shared_ptr<int>& data)
{
age = data;
}
void print()
{
std::cout << *age << std::endl;
std::cout << "Use count: " << age.use_count() << std::endl;
}
};
int main()
{
// here we store the data
int i = 17;
// here we have the shared_ptr
std::shared_ptr<int> p = std::make_shared<int>(i);
// here we pass it to the object
test t(p);
t.print();
return 0;
}
Parsing a file and need to add students to a struct vector using an array for student names specific to that course line.
In my course.h file:
struct Course {
std::string name;
int enrollment;
int maxEnrollment;
std::string* students; ///< array of student names
Course(std::string courseName, int maxEnrollmentPermitted);
bool enroll(std::string studentName);
void print(std::ostream& output);
};
In my course.cpp file:
bool Course::enroll(std::string studentName) {
this->students = new std::string[studentName];
if (this->enrollment < this->maxEnrollment) {
this->enrollment++;
return true;
}
else {
return false;
In my source file:
void processEnrollmentRequests(istream& enrollmentRequestsFile, vector<Course>& courses) {
// Read the requests, one at a time, serving each one
string courseName;
enrollmentRequestsFile >> courseName;
while (enrollmentRequestsFile) {
enrollmentRequestsFile >> ws;
string studentName;
getline(enrollmentRequestsFile, studentName);
int pos = search(courses, courseName);
if (pos >= 0) {
// Found a matching course
bool enrolled = courses[pos].enroll(studentName);
if (enrolled) {
cout << studentName << " has enrolled in " << courseName << endl;
}
else {
// course is full
cout << studentName << " cannot enroll in " << courseName << endl;
}
}
else {
// course does not exist
cout << studentName << " cannot enroll in " << courseName << endl;
}
enrollmentRequestsFile >> courseName;
}
}
}
}
I cant seem to add the gathered studentName to the array using this->students = new std::string[studentName]. Getting an error that says must have integral or enumeration type.
new SomeThing[size] is used to declare array. It makes no sense to use a string as the size.
Assuming the size of students is limited to maxEnrollment, you can use this:
if (this->enrollment < this->maxEnrollment) {
this->students[this->enrollment++] = studentName;
return true;
}
else {
return false;
For the sake of completeness, the allocation of students is not the only problem. Given that the code you posted also uses std::vector<Course>, and Course does not follow the rule of 3, using it in a std::vector is highly likely to cause memory corruption, leaks, etc.
Given that you state that students must remain a pointer, the complete fix is to write Course in this manner:
#include <string>
#include <algorithm>
struct Course {
std::string name;
int enrollment;
int maxEnrollment;
std::string* students; ///< array of student names
Course(std::string courseName, int maxEnrollmentPermitted);
bool enroll(std::string studentName);
void print(std::ostream& output);
Course(const Course& rhs);
Course& operator =(const Course& rhs);
~Course();
};
Course::Course(const Course& rhs) : name(rhs.name),
enrollment(rhs.enrollment),
maxEnrollment(rhs.maxEnrollment),
students(new std::string[rhs.maxEnrollment])
{
for (int i = 0; i < maxEnrollment; ++i)
students[i] = rhs.students[i];
}
Course& Course::operator= (const Course& rhs)
{
Course temp(rhs);
std::swap(temp.students, students);
std::swap(temp.maxEnrollment, maxEnrollment);
std::swap(temp.enrollment, enrollment);
std::swap(temp.name, name);
return *this;
}
Course::~Course() { delete [] students; }
Course::Course(std::string courseName, int maxEnrollmentPermitted) :
name(courseName),
enrollment(0),
maxEnrollment(maxEnrollmentPermitted),
students(new std::string[maxEnrollmentPermitted])
{}
Why all of this code? Well, in the code you posted in your question, you are using a std::vector<Course>. The Course class as written could not be safely used in a vector, due to Course having incorrect copy semantics. Thus your error you're getting may have a lot to do with code you stated wasn't yours (the vector<Course>).
The adjustments to Course above now makes Course safe to be used in a vector since the copy semantics (copy constructor, assignment operator, and destructor) have now been implemented to handle the dynamically allocated students member.
Note that absolutely none of this code would be necessary if students were simply a std::vector<std::string> instead of std::string *.
For more reading:
What is the rule of 3?
What is the copy / swap idiom?
So here is my program. I have to make an object of type Student, then have the Student "check out" an item. And I am using an overloaded addition operator to make the user check out that item.
main.cpp:
#include <iostream>
#include "Student.h"
using namespace std;
int main() {
Student s(54000, "JOHN", "DOE");
cout << "main:" << endl << (s + "Frisbee") << endl << endl;
system("pause");
return 0;
}
I defined all my class defintions in the header file to try and keep this program minimal and simplified.
Student.h:
#ifndef STUDENT_H
#define STUDENT_H
#include <fstream>
#include <string>
#include <iostream>
using namespace std;
class Student {
public:
string firstName;
string lastName;
int id;
int itemsCheckedOut;
int size;
string *array;
Student(int id = 0, string firstName = "", string lastName = "") {
Student::firstName = firstName;
Student::lastName = lastName;
Student::id = id;
itemsCheckedOut = 0;
size = 10;
array = new string[size];
}
Student(const Student &other) {
itemsCheckedOut = other.itemsCheckedOut;
array = new string[itemsCheckedOut];
for (int i = 0; i < itemsCheckedOut; i++) {
array[i] = other.array[i];
}
}
~Student() {
delete[] array;
array = NULL;
}
Student &operator=(const Student &rhs) {
if (this != &rhs) {
firstName = rhs.firstName;
lastName = rhs.lastName;
id = rhs.id;
itemsCheckedOut = rhs.itemsCheckedOut;
delete[] array;
array = new string[size];
for (int i = 0; i < itemsCheckedOut; i++) {
array[i] = rhs.array[i];
}
}
return *this;
}
void CheckOut(const string &item) {
array[itemsCheckedOut] = item;
itemsCheckedOut++;
}
friend ostream &operator<<(ostream &output, const Student &student) {
output << student.id << " " << student.firstName << " " << student.lastName << endl;
if (student.itemsCheckedOut != 0) {
output << student.itemsCheckedOut;
for (int i = 0; i < student.itemsCheckedOut; i++) {
output << " " << student.array[i] << endl;
}
}
else {
output << 0;
}
return output;
}
const Student operator+(const string &item) {
Student s;
s = *this;
s.CheckOut(item);
cout << "class:" << endl << s << endl << endl;
return s;
}
};
#endif
output:
class:
54000 JOHN DOE
1 Frisbee
main:
-858993460
1 Frisbee
As you can see, from the main, its outputting the wrong thing. Instead of outputting the id followed by two spaces then the first name and last name, it outputs the number: -858993460. This has gotta be some sort of memory leak issue or something, but I'm pretty sure my copy constructor, overloaded assignment operator, and deconstructor are all defined correctly, but you can take a look at them.
I would appreciate any help at all as I am getting pretty desperate here. Thanks.
Your actual operator+ looks correct. But there are bugs in your copy-constructor and assignment-operator that would cause it to malfunction:
The copy-constructor does not set size, id, or the names.
The copy-constructor should allocate [size] items, not [itemsCheckedOut].
The assignment operator does not copy size.
The assignment operator allocates a new array whose dimension is the old size, probably causing an immediate buffer overflow.
The checkOut function does not check that it doesn't write beyond size. It needs to detect this case and either reject the checkout, or allocate more space. (I mentioned this last time you posted a question about this project)
It calls copy constructor:
Student(const Student &other) {
itemsCheckedOut = other.itemsCheckedOut;
array = new string[itemsCheckedOut];
for (int i = 0; i < itemsCheckedOut; i++) {
array[i] = other.array[i];
}
}
but you forget to copy all Student's fields in its body.
You override default copy constructor, so you should manually execute all data copying, as in assignment operator.
You should replace your string* array with a std::vector. It will handle the memory management for you, make your code far easier and less error prone than the manual memory management you are currently using. You can reserve an initial size of 10 if you are worried about it doing allocations when adding items (although with such small data sizes that shouldn't ever be a problem).
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 :)
I'm having an issue with overloading the << operator. Everything prints and enters fine, but when I try and return the ostream, I get this error:
Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
I've also already overloaded another << operator in this project that has returned an ostream just fine. This operator isn't used in the following code. Here's the code:
#include "header1.h"
#include <iostream>
using namespace std;
class Car
{
public:
friend class Extras;
friend int main();
friend ostream& operator<< (ostream& os, const Car& in);
Car();
Car(string in_name, int in_year, string in_color, float in_cost);
private:
string name, color;
int year, extr_num;
float cost;
Extras *extr;
};
int main()
{
Car c1;
cout << c1;
return 0;
}
//Default Constructor
Car::Car()
{
name = "TEMP";
color = "BLUE";
year = 0;
cost = 0;
extr = new Extras[3];
extr_num = 0;
}
//Constructor
Car::Car(string in_name, int in_year, string in_color, float in_cost)
{
name = in_name;
color = in_color;
year = in_year;
cost = in_cost;
extr = new Extras[3];
extr_num = 0;
}
//Overloaded << operator for Car class
//This function is the one that fails.
ostream& operator<< (ostream& os, const Car& in)
{
os.precision(2);
os << in.name << ", " << in.year << ", "
<< in.color << ", $"<< in.cost << ", ";
os << "extras include: ";
os << endl;
return os; //Line of code in question
}
This bit of code in the other header works perfectly fine:
ostream& operator<< (ostream& os, Extras const &in)
{
os << in.ex_list;
return os;
}
Everything prints to the screen fine before the return. And these two functions look the same to me, can someone more experience with C++ tell me otherwise?
There's nothing in the shown code that will cause the problem you describe. The "_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)" error is an indication that the heap was corrupted at an earlier point, it's being detected at your return statement but isn't otherwise related to the code in your operator<<
You've hosed your heap. It may or may not have anything to do with the code currently running. Don't see anything immediately apparent in what you've decided to show us that would cause it though I'd start with any use of raw pointers.