Hi all, I'm currently working on a C++ project for a 3rd year Physics course module. The project is a family tree, in which I have an abstract base class and two derived classes.
In the main I use this code to create one of the derived classes:
// Create a pointer to an (abstract base class) ftree variable with up to 50 records
ftree *a[50];
cout << "How many records would you like to input? ";
cin >> rec;
// Determine which type of record will be produced
for ( int i = 0; i < rec; ++i) {
while ( rtype != 'h' && rtype != 'l' ) {
cout << "\nRecord Type ('h'/'l'): ";
cin >> rtype;
}
// Create human / plang type record
if( rtype == 'h' ) { a[i] = new human; }
else { a[i] = new plang; }
}
My problems arise thus:
1. How can I create an accessor in the BASE class for a function in a derived class of a particular type?
e.g. In the derived class: string getDev() { return dev; }
I've tried using a virtual function in the base class to access this function but it doesnt work.
Ive tried: Virtual string getDev{};
2. The following function allows the user to search the data that has been input and return the record(s) containing the corresponding data.
The function takes arguments: findit(field to search, array to be searched, field accessor)
The function is templated so that it can operate on any type of data
template <class T1, class T2>
void findit(T1 x, T1 y, T2 z) {
if x = y { z->showInfo(); }
} // End of findit() function
Then in the main I've got:
char temp[50] = 'John';
for (int i = 0; i < rec; ++i) {
findit<string,ftree>( temp, a[i]->getName(), a[i] );
}
This compares the string 'John' with the name element of my a[i] array (as created above) but it fails and says:
error C2770: invalid explicit template argument(s) for 'void findit(T1,T1,T2)
Please post actual code. It's hard to figure out what's wrong with just a paraphrase.
Here's what I can gather.
1) Need to see code, not sure what your problem is.
2) You should call findit like this:
findit( temp, a[i]->getName(), a[i] );
You do not need to specify the template types (findit<>) because the compiler can
deduce them from your actual parameter list. The only times you need to specify
the types are:
1) When calling templated constructors, argument deduction does not take place;
2) When your function's return type is templated;
3) When the compiler cannot deduce the type of the actual parameter.
Thanks for your responses, unfortunately I still can't get either bit to work, I've been at it non stop since 9am so I really am trying! Okay, here's the whole code, hopefully sufficiently annoted for all to grasp! I've excluded the dob class files which are inherited by the ftree class cus all that works fine and I need to keep within the word limit!
// This is the abstract base class for family tree records.
// This class contains general information that would apply to any type of family tree.
// Contains accessors to the data in the inherited classes.
#ifndef FTREE_H
#define FTREE_H
// The following two bits of code eliminate the strcpy errors in the newer versions of Visual C++ Express Edition
#define _CRT_SECURE_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#include <string>
#include "dob.h"
using namespace std;
class ftree : public dob {
friend void userhelp();
template <class T1, class T2>
friend void myfindit(T1 x, T1 y, T2 z);
protected:
char name[50];
char parent[50];
char child[50];
dob date;
int recnum;
// Accessors THESE DONT WORK
//virtual string getDev() = 0;
//virtual string getLev() {};
//virtual int getYDev() {};
}; // End of ftree class
#endif
**************************************************************
/* This header file defines the human family tree class which is inherited
from the abstract family tree class. For example, considering the Java language,
a parent could be C/C++ and a child (daughter) could be JSP.
This class is derived from the ftree abstract base class. */
cout << "\tDate of Birth: " << date << endl;
cout << "\tRelatives: " << endl;
cout << "\t\tParent: " << parent <<endl;
cout << "\t\tPartner: " << partner << endl;
cout << "\t\tChild: " << child << endl;
}
************************************************************
/* This header file defines the programming language family tree class which is
inherited from the abstract family tree class. For example, considering the
Java language, a parent could be C/C++ and a child (daughter) could be JSP.
This class is derived from the ftree abstract base class. */
#ifndef PLANG_FTREE_H
#define PLANG_FTREE_H
#include "ftree.h"
#include "dob.h"
#include <string>
using namespace std;
class plang : public ftree {
private:
char dev[20]; // Developed by
char lev[1]; // Language level (high/low)
int ydev; // Year developed
void showInfo(void); // Defined in plang.cpp
};
#endif
***********************************************************
/* This code overrides the showInfo() function in the 'plang.h' header file.
The showInfo() function displays the information about a programming language
input by the user. */
#include "plang.h"
#include <iostream>
using namespace std;
// This is the main code file where the data is input by and displayed to the user.
#include "ftree.h" // Include the general family tree class
#include "dob.h" // Include the date of birth class (for dd/mm/yy format)
#include "human.h" // Include the human family tree class
#include "plang.h" // Include the c-evolution family tree class
#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
int main()
{
char rtype = 'z'; // Record type
char temp[50]; // temporary input variable to store text
int rec = -1;
int year = -1; // Variable to store year of birth/development
ftree *a[50]; // Create a pointer to an ftree variable with up to 50 records
cout << "How many records would you like to input? ";
cin >> rec;
// If user does not wish to input any records terminate program
if ( rec == 0 ) { return -1; }
for ( int i = 0; i < rec; ++i) {
// Determine which type of record will be produced
while ( rtype != 'h' && rtype != 'l' ) {
cout << "\nRecord Type ('h'/'l'): ";
cin >> rtype;
}
// Create human / plang type record
if( rtype == 'h' ) { a[i] = new human; }
else { a[i] = new plang; }
// Allocate record number to record
a[i]->setRecNum(i);
cout << "\nEnter the following information:" << endl;
// Name
cout << "\nName: ";
cin >> temp;
a[i]->setName(temp);
// If human family tree member
if( rtype == 'h' ) {
char q = 'z'; // new character for post-data input display/search questions
// Ask the user if they would like to display all records
while ( q != 'n' ) {
cout << "\nDisplay all records? (y/n) ";
cin >> q;
if ( q == 'y' ) {
for (int i = 0; i < rec; ++i) { a[i]->showInfo(); }
q = 'n'; // Set q to 'n' to terminate the loop
}
}
q = 'z'; // Reset q
// Ask the user if they wish to search the database.
// The findit() function is defined in ftree.h
while ( q != 'n' ) {
cout << "\nWould you like to search the database? (y/n): ";
cin >> q;
if ( q == 'y' ) {
cout << "\nWhat field would you like to search? (Type 'h' for help): ";
cin >> q;
if ( q == 'h' ) { userhelp(); }
if ( q == 'n' ) {
cout << "\nWhat name would you like to search for? ";
cin >> temp;
for (int i = 0; i < rec; ++i) { myfindit<string,ftree>( temp, a[i]->getName(), a[i] ); }
}
if ( q == 'g' ) {
cout << "\nWhich gender would you like to search for (m/f)? ";
cin >> q;
//for (int i; i < rec; ++i ) { findit(temp,a,a[i]->getGender) }
}
} // End of search if()
} // End of search while()
return -1; // Terminate program
} // End of main()
//The following function displays instructions on how to choose which data field to search.
void userhelp() {
cout << "\nGeneral Fields:" << endl;
cout << "\tType 'n' to search for a name" << endl;
cout << "\nHuman Fields:" << endl;
cout << "\tType 'g' to search for a gender" << endl;
cout << "\tType 'd' to search for a date of birth" << endl;
cout << "\tType 'p' to search for a parent" << endl;
cout << "\tType 'pr' to search for a partner" << endl;
cout << "\tType 'c' to search for a child" << endl;
cout << "\nProgramming Language Fields:" << endl;
cout << "\tType 'l' to search for a language level" << endl;
cout << "\tType 'dv' to search for a developer" << endl;
cout << "\tType 'y' to search for a development year" << endl;
cout << "\tType 'pl' to search for a parent language" << endl;
cout << "\tType 'dl' to search for a daughter language" << endl;
} // End of help() function
/* The following function allows the user to search the data that has been input and return
the record containing the corresponding data.
The function takes arguments: findit(field to search, array to be searched, field accessor)
The function is templated so that it can operate on any type of data*/
template <class T1, class T2>
void myfindit(T1 x, T1 y, T2 z) {
There are solutions to your problem #1 that require complete redesigns, but short of that, your simplest solution is probably a downcast.
1 2 3 4 5 6 7
// Given:
ftree* a[ 50 ];
// To call accessor getPartner() on human, you'd need to:
human* h = dynamic_cast<human*>( a[ i ] );
if( h )
cout << "Partner is " << h->getPartner() << endl;
Absolutely wonderful! Thank you so much! That works like a charm, and as for the other problem it took MANY hours but I've figured it out by myself - so rewarding when that finally happens (IF it ever does!).
Thanks so much everyone, I've just got to figure out how to implement a comparison between two dates of birth, I think I'll pose that little question as a separate topic as I doubt many people will get past the horrendous amount of code above!
EDIT - Okay now that its fully implemented its coming up with a debug error when I run the program...
I added this to the code:
for ( int i = 0; i < rec; ++i) {
// Determine which type of record will be produced
while ( rtype != 'h' && rtype != 'l' ) {
cout << "\nRecord Type ('h'/'l'): ";
cin >> rtype;
}
// Create human type record
human* h = dynamic_cast<human*>(a[i]);
// Create plang type record
plang* p = dynamic_cast<plang*>(a[i]);
// Allocate record number to record
h[i].setRecNum(i+1);
Well, I've just finished figuring out your old code... but if you've scrapped all that I'll just go away.
OK, I'll list your major problem with that template function (and issue #1 also):
You cannot instantiate abstract classes. Hence, with the myfindit(), you need to write it as:
1 2 3 4 5 6
template <class T1, class T2>
void myfindit(T1 x, T1 y, T2 z) {
if (x == y) { z.showInfo(); }
} // End of findit() function
Notice the proper syntax and the fact that T2 is not a pointer (since you'll be using an abstract ftree... and template functions have to have something solid to work with.
Use it thus:
No instance of ftree need be instantiated; all that is needed is a reference to an existing ftree descendant.
As to your latest problem, if that is exactly how your code appears: keep in mind that a human is still a human even if you call it a plang, and vice-versa. Hence, don't cast a human to a plang, and don't cast a plang to a human. They are not compatible, so keep them separate.
Hey, sorry, I've taken note about using code tags for future reference!
Thanks for your help - I spent a long time implementing this as my program is now pretty big. It sort of worked but for some reason I couldn't stop it getting errors after inputting more than one record.
Also, the fact that my find function has to be outside the switch while the human/plang instances are inside the switch means that I cant use
since h nor p no longer exist... this seems far too big a problem to overcome in one fleeting night, so thank you VERY much for your help, but I'm gonna have to just leave it and write about what I ATTEMPTED to achieve in the report.
The bottom line is I still have a find function, but it can only access variables within the base class itself... unless there is a way to access the variables in the inherited classes from the base class as I originally enquired?
Thanks again all for your replies, thats a LOT of code up there, I really appreciate your help!
Heh, sorry for the grief. You are tackling a lot at once.
You can still use myfindit(), since getName() is inherited from ftree.
As for large projects, don't worry about it. I just finished the first draft of a little utility (in Delphi) for playing with the Desktop wallpaper that contains 25 code files (most less than 300 lines, a couple around 1200 lines, and the main program a little over 4400), 6 form definition files, 4 resource files, and a slew of other small files (autorun.inf, config files, project definition files, etc).
As long as you keep things organized and separate things separate, it really isn't too much at all.