Reputation: 381
When I try to run this code:
Instructor.cpp:
#include "Instructor.h"
#include "Person.h"
using std::string;
Instructor::Instructor() {
Person();
salary = 0;
}
Instructor::Instructor(string n, string d, string g, int s) {
Person(n, d, g);
salary = s;
}
void Instructor::print() {
if (gender == "M")
std::cout << "Mr. ";
else
std::cout << "Ms. ";
Person::print();
std::cout << " Instructor, Salary: " << salary << std::endl;
}
Instructor.h:
#include <iostream>
#include <string>
class Instructor: public Person
{
public:
Instructor();
Instructor(std::string n, std::string d, std::string g, int s);
virtual void print();
protected:
int salary;
};
Person.h:
#include <iostream>
#include <string>
class Person
{
public:
Person();
Person(std::string n, std::string d, std::string g);
virtual void print();
protected:
std::string name;
std::string dob;
std::string gender;
};
I receive these errors:
In file included from Instructor.cpp:1:0:
Instructor.h:5:1: error: expected class-name before ‘{’ token
{
^
Instructor.cpp: In member function ‘virtual void Instructor::print()’:
Instructor.cpp:16:6: error: ‘gender’ was not declared in this scope
if (gender == "M")
^
Instructor.cpp:20:16: error: cannot call member function ‘virtual void Person::print()’ without object
Person::print();
All three of these errors are confusing me. If the Instructor class was derived from Person, and within Person the gender field is protected, then why am I receiving error: ‘gender’ was not declared in this scope
, as well as error: cannot call member function ‘virtual void Person::print()’ without object
?
I feel like I'm doing something obviously wrong here, such as including the files incorrectly or something like that. Any help is appreciated.
Upvotes: 8
Views: 23817
Reputation: 6690
Each time the compiler encounters the #include "FileName.h"
preprocessor directive, the compiler attempts to re-load - and, I believe, redefine - the included class. This will result in a compile-time error. You can read more in detail about include guards or the wrapper #ifndef, here.
In order to "guard" against this, we use include guards - which must appear before any of your code in your header file.
For example:
#ifndef CLASSIDENTIFIER_H // Capitalized letters are convention (see below)
#define CLASSIDENTIFIER_H
#include <iostream>
#include <cstdlib>
public ClassIdentifier
{
// Class Member Declarations
};
#endif
The #ifndef
stands for "if not defined". The following identifier - in all capitals for no reason other than 'that's how it's done' - is by convention also named according to the class name (this enables the compiler to check a number of things, primarily ensuring that - if everyone follows the convention - that no other class of the same name has been included, referenced, or defined in the program.
Logically, one can deduce that the next two include guards stand for "define" and "end if not defined".
If you include your #endif
immediately after the close of your class declaration, e.g. };#endif
(see above) this will also result in a compile-time error and one that may not be readily apparent (though, I think the compiler is pretty clear with its resulting error message).
The other code I included is, of course, just for the sake of familiarity and a demonstration of what is possible.
Upvotes: 1
Reputation: 7625
You have to include person.h
in instructor.h
, otherwise the token Person
is unknown to compiler. And when you do this, make sure you remove person.h
from instructor.cpp
. Otherwise you will get re-declaration error. But common practice is to use #ifdef
directives in header files to prevent multiple inclusion. like in person.h
#ifndef PERSON_H
#define PERSON_H
/// class definition and other code
#endif //PERSON_H
or you can use #pragma once
in VC++.
Another error is you are not initializing Person
portion of Instructor
correctly. In the constructor :
Instructor::Instructor(string n, string d, string g, int s) {
Person(n, d, g); // this will create a temporary `Person` object instead of initializing base part and
// default constructor of `Person` will be used for this `Instructor` object
salary = s;
}
do it like this
Instructor::Instructor(string n, string d, string g, int s): Person(n,d,g), salary(s)
{ }
Upvotes: 8
Reputation:
A few things need to be fixed:
Instructor.h
needs to have #include "Person.h"
. It is needed there so Instructor
understands what it inherits from. No need for this include to exist in Instructor.cpp
if you move it to Instructor.h
as Instructor.cpp
has Instructor.h
included.I am not sure if you intended for Person
to be an abstract base class, but I don't see a Person.cpp
or any implementation of any member functions in Person.h
. Even if you want Person
to be an abstract base class, you need to give a function body to the constructor even if it is empty.
Person() {};
You are calling Person::print
in Instructor::print
without it having an implementation.
When using inheritance, you need to utilize member initialization list which calls the base class constructor to initialize the base class's member variables. In both Instructor constructors, using the Person
constructors in the function body as you are currently doesn't lead to the desired result.
Furthermore, using initialization lists is more efficient and the accepted convection for setting the class's own member variables.
Instructor::Instructor() : Person(), salary(0) { }
Instructor::Instructor(string n, string d, string g, int s) : Person(n, d, g), salary(s) { }
Upvotes: 0
Reputation: 9
Isnt it just the order of including header files in Instructor.cpp? You will have to include Person.h first and then Instructor.h. Thats It.
Upvotes: -1
Reputation: 137425
Several issues:
Instructor
class, your Person
class isn't defined yet. You should #include "Person.h"
at the start of Instructor.h
, which leads to the second issue...Your headers are not protected against double includes (which is the error you see in your comment above). To fix this, you need an include guard:
#ifndef PERSON_H
#define PERSON_H
class Person {
// ...
};
#endif
The second time this file is included, PERSON_H
is already defined, so what's between the #ifndef
and the #endif
will be ignored.
You are calling the base class constructor incorrectly. The correct syntax is:
Instructor::Instructor(string n, string d, string g, int s) : Person(n, d, g){
salary = s;
}
What you are doing (writing Person(n, d, g);
in the constructor body) will compile, because if you do not explicitly call the base class constructor, the compiler will try to call the parameterless default constructor for you, and in your case, Person
happens to have a default constructor that can be called (if Person
does not have a constructor that takes no parameters, you'll get a compile error). However, the effect of that statement is simply to create a temporary Person
object that gets destructed at the end of the statement, not call the base class constructor.
This syntax (called a member initialization list) can also be used to initialize other class members, and in fact is the preferred way of doing so:
Instructor::Instructor(string n, string d, string g, int s) : Person(n, d, g), salary(s) { }
Upvotes: 2