Thread: Pointer and Polymorphism help.

  1. #1
    Registered User
    Join Date
    Dec 2008
    Posts
    13

    Smile Pointer and Polymorphism help.

    Hello, this board has helped me before while I lurked around so I hope you can help me with my current project. This is my first time using Polymorphism/virtual and second time using pointers. When I execute the program, I don’t know if I’m using either perfectly to be honest. I get errors like…
    1>library.obj : error LNK2005: "public: __thiscall Holding::Holding(char *,int)" (??0Holding@@QAE@PADH@Z) already defined in holding.obj
    And one like…
    1>C:\...\c++\Polymorphism\Polymorphism Lab\Debug\Polymorphism Lab.exe : fatal error LNK1169: one or more multiply defined symbols found

    Any tips would be appreciated.


    My Main():
    Code:
    // library.h
    
    #ifndef _LIBRARY_H
    #define _LIBRARY_H
    
    #include <stdlib.h>
    #include <iostream>
    #include <fstream>
    
    #include "holding.h"
    //cpp's taken out, what the hell was I thinking?
    #include "book.h"
    
    
    	Holding *holdPtr[5];
    	Book *myBook;
    #endif
    
    //-------------------------CPP BELOW--------------------------------
    // library.cpp
    
    #include <iostream>
    #include <fstream>
    #include <stdlib.h>
    #include "library.h"
    
    ofstream csis;
    
    int main(){
        
        csis.open("csis.dat");
    	void getInfo(int);
    
    	for (int i=0;i<5;i++){
    		getInfo(i); }
    	for (int i=0;i<5;i++){
    		//cout holdPtr[i]->print();
    		holdPtr[i]->print(); }
        csis.close();  
    }
    
    void getInfo(int x){
        char *titleTemp;
        int callNumTemp;
        char choice;
        char *authorTemp;
    
    	cout << "Enter holdings type: " << endl;
    	cout << "/'b/' for book and /'r/' for recordings: ";
    	cin >> choice;
    
    	if (choice == 'b' || choice == 'B') {
    		cout << "Title: " << endl;
    		cin >> titleTemp;
    		cout << "Author: " << endl;
    		cin >> authorTemp;
    		cout << "CallNum: " << endl;
    		cin >> callNumTemp;
    
    		myBook = new Book(titleTemp,authorTemp,callNumTemp);
    		holdPtr[x] = myBook;
    		myBook->~Book();
    
    	} else 
    		cout << "else called"<< endl;
    
    
    }
    My Abstract base class:

    Code:
    // holding.h
    
    #ifndef _HOLDING_H
    #define _HOLDING_H
    
    #include <stdlib.h>
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    class Holding{
          
    protected:
    	char *title;
    	int titleLength;  
    	int *callNumber; //*
    
    public:
         Holding(char*, int);
         Holding();
         virtual ~Holding();
         virtual void print(/*ostream &*/) = 0;  
    };
    
    #endif
    
    //-------------------------CPP BELOW--------------------------------
    // holding.cpp
    
    #include "holding.h"
    using namespace std;
    extern ofstream csis;
    
    Holding::Holding(char *tempTitle, int tempCallNumber){ 
                          
    	titleLength = strlen(tempTitle);
    	title = new char[titleLength + 1];
    	strcpy(title, tempTitle);
    	title[titleLength] = '\0';
    
    	*callNumber = tempCallNumber;
    }
    
    Holding::~Holding(){  
        delete [] title;
    }
    My derived class:

    Code:
    // Book.h
    
    #ifndef _BOOK_H
    #define _BOOK_H
    
    #include <stdlib.h>
    #include <iostream>
    #include <fstream>
    
    #include "holding.h"
    extern char *author;  //NEW NEW
    extern int authorLength;
    class Book : public Holding {
          
    protected:
    	//char *author;
    	//int authorLength;
    
    public:
    	Book(char*, char*, int);
    	Book(const Book &);
    	~Book();
    	virtual void print(/*ostream &*/);  //ostream is req for project
    // but for the time being, ignoring to fix  other problem.
    
    };
    
    #endif
    
    //-------------------------CPP BELOW--------------------------------
    // Book.cpp
    
    #include "book.h"
    using namespace std; //new
    extern ofstream csis; //new
    	char *author; //NEW NEW
    	int authorLength;
    Book::Book(char *title, char *tempAuthor, int callNumtemp) :  Holding(title, *callNumber) {
                    
        authorLength = strlen(tempAuthor);
        author = new char[authorLength + 1];
        strcpy(author, tempAuthor);
        author[authorLength] = '\0';  
    }
    
    Book::~Book(){
    	delete [] author;        
    }
    void Book::print(/*ostream &out*/){
            
            cout << "Book: " << author << " " << '\"' << title << '\"' << " " << callNumber << endl;
            csis << "Book: " << author << " " << '\"' << title << '\"' << " " << callNumber << endl;  //uncommented
    }
    Last edited by Skyy; 12-06-2008 at 02:51 PM.

  2. #2
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Don't include cpp files. Remember, cpp files are compiled, h files are included (pasted into cpp files).

    Also, the globals declared in "library.h" might need to be declared extern and then defined in one cpp file.

    Of course, having all these globals don't make for robust code - and code others will enjoy figuring out. For example, why is csis global if it is only used in main?
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  3. #3
    Registered User
    Join Date
    Dec 2008
    Posts
    13

    Smile

    Thank you, Anon, for the quick reply.

    Quote Originally Posted by anon View Post
    Also, the globals declared in "library.h" might need to be declared extern and then defined in one cpp file.
    I fixed the first and third concerns, what was I thinking when including the cpp's?! Pardon my inexperience but I don't know what 'declared extern' means...

  4. #4
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    It means something like this:
    Code:
    // X.cpp:
    int myint;
    
    // X.h:
    extern int myint;
    This makes the global defined in X.cpp available to all source files including X.h.
    If you don't do it this way, you will get multiple globals of the same name and the linker will complain.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  5. #5
    Registered User
    Join Date
    Dec 2008
    Posts
    13
    Thanks Elysia, my code has been changed and first post reflects that change. Would it be wise to also declare Holding *holdPtr[5]; and Book *myBook; in library as extern?


    The program now starts but gives me warnings like "Run-Time Check Failure #3 - The variable 'titleTemp' is being used without being initialized."

    And then a forced break after inputting the title and pressing enter, "Unhandled exception at 0x6984653e (msvcp90d.dll) in Polymorphism Lab.exe: 0xC0000005: Access violation writing location 0xcccccccc." and opens is stream at line 1010 "_Ch = _Traits::to_char_type(_Meta); // got a character"

    I'll try to figuring it out and will post a little bit later but will check this thread if anyone has an tip to help me out with these erros. 'til then.

  6. #6
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Code:
    char *titleTemp;
    ...
    cin >> titleTemp;
    You have an uninitialized pointer that doesn't point anywhere in particular and you try to store something at that place. (You need to allocate some memory to store the data first.)

    Unless, you are required to do it this way (no strings, containers etc), good luck with debugging.
    Last edited by anon; 12-06-2008 at 04:09 PM.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  7. #7
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    The fun part about pointers is that they just point somewhere. Currently, your TitleTemp variable points to New Jersey. You want it to point to memory somewhere inside your computer. You could use new, like you've done elsewhere, but that's just wallpapering over the real problem -- the real problem is that you don't want char *, you want std::string.

  8. #8
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by Skyy View Post
    The program now starts but gives me warnings like "Run-Time Check Failure #3 - The variable 'titleTemp' is being used without being initialized."
    For the record, it means that you tried to use a variable that you didn't initialize. That variable will contain garbage and thus unlikely to produce wanted results, and Visual studio warns you about that.

    Also, I missed it at first, but you may want to take a look at:
    http://cpwiki.sf.net/Don't_remove_parameter_names

    Also, set your warnings to maximum. Project properties -> C/C++ -> General -> Warnings (to Level 4). It provides valuable diagnostic output.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  9. #9
    Registered User
    Join Date
    Dec 2008
    Posts
    13

    Smile

    Good day.

    I've replaced:
    cout << "Title: " << endl;
    cin >> titleTemp;

    With:
    cout << "Title: " << endl;
    titleTemp = new char[20]; //numbers for testing, change later.
    cin.getline(titleTemp, 5);

    However, it now skips asking for the titleTemp input and assigns it '0', it does ask for the authorTemp which is identical and below the titleTemp lines above...

    I'm really struggling with getting the input to the constructor.

    @Elysia: Both recommendations applied, I don't see the edit botton on the original post so I can't change it. Thanks for the tips.

    @tabstop: Since my prof. did not teach us about it and the instructions requires destructors for each holding, I don't think I can use std::string. That being said, I did try it anyway (Googled it) and ran into errors. So I reverted back.

    In this assignment, you will learn how to use inheritance and virtual functions by designing a set of classes to represent library holdings. Our library has two kinds of items: books and audio recordings. Each item has a title and an integer call number. In addition, books have authors, and recordings have performers and formats (LP, Cassette, Reel-to-Reel, or Compact Disk). In addition to constructors and destructors[me: destructors, means I need to use char *???], each holding should have a print() function that displays its data to an ostream object (such as cout) that the user specifies.

    You should begin by designing a Holding class that contains all the data common to books and recordings [me: title and call number]. It should also have a pure virtual print() function. Then design the Book and Recording classes to add the details necessary for those types of items and to implement the print() function. Note that Holding will be an abstract base class, since it will have a pure virtual function. This makes sense, since there are no generic holdings, only books and recordings.

    All three classes should have both copy constructors and constructors that accept the individual data fields as arguments.[me: which means in a group, (title, author, callnum)] Although it may seem natural to have a zero-argument default constructor that queries the user and reads in the data fields, doing I/O from within a constructor is probably unwise. If the constructor did I/O, the class implementation would be tied to a particular style of getting data. Applications that use this class may read input from a file, in which case the prompt messages would be inappropriate, or they may use a window-based interface that does not support the cin and cout I/O stream objects. Since constructors are such a basic part of the operation of a class, it is best to let the user decide where their input data comes from. [me: so, userinput in library, function used by main()?]

    Once you have written the code to implement the Holding, Book, and Recording classes, you should write a simple program[me: library] to test them. This program should declare an array of five pointers to Holding objects [me:Holding *holdPtr[5];] and then repeatedly call a function that creates a new Holding[myBook = new Book(titleTemp,authorTemp,callNumTemp);??? creates book, should be holding?]. This function asks the user which kind of Holding is to be entered and then inputs the data needed to create either a Book or a Recording. A new object of the appropriate class is created with this data, and the resulting pointer is returned. Note that even though the function always returns either a Book pointer or a Recording pointer, the function can be declared to return a Holding pointer, and the pointer to the newly created object can be returned without conversion. This pointer is then stored in the array and the function is called again until all five objects have been created. Now there is an array to five pointers to Holding objects, and each of these objects "knows" whether it is a Book or a Recording, although the program has no direct way of querying them to get this information. However, it can run through the array and call the virtual print() function for each object, which will then use the appropriate virtual function for printing itself out. A sample session is shown below, with user responses shown in bold type for clarity.
    Last edited by Skyy; 12-07-2008 at 02:43 PM.

  10. #10
    The larch
    Join Date
    May 2006
    Posts
    3,573
    cout << "Title: " << endl;
    titleTemp = new char[20]; //numbers for testing, change later.
    cin.getline(titleTemp, 5);
    You allocate 20 characters and then only allow 5 (4?) to be input. Quite possibly if you try to enter a longer string, cin will go in a fail state. You have to call cin.clear() before you can continue using it but I don't know if the input is lost because of the failure. Input into char array is very capricious compared to std::string.

    Perhaps, since you are going to allocate memory for the input in Book too and copy it, you could use just a plain stack char array which is a bit larger than 20 characters.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  11. #11
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    You don't really need pointers at all.
    char string[50];
    std::cin.getline(string, sizeof(string));

    Or better

    std::string string;
    std::getline(std::cin, string);

    Quote Originally Posted by Skyy View Post
    @Elysia: Both recommendations applied, I don't see the edit botton on the original post so I can't change it. Thanks for the tips.
    It usually does that keep newbies from editing out their posts once they've gotten an answer to their questions...

    @tabstop: Since my prof. did not teach us about it and the instructions requires destructors for each holding, I don't think I can use std::string. That being said, I did try it anyway (Googled it) and ran into errors. So I reverted back.
    The assignment says you must use destructors, not what the destructors must do. I see nothing in it that forbids the use of std::string.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  12. #12
    Registered User
    Join Date
    Dec 2008
    Posts
    13

    Smile Well, using std::string seems like just the right way to go.

    Thanks for the quick replies, you guys rock. I tried it earlier using code similar to Elysia's first example, now I'm using a style similar to the second example but getting the same error both times:

    \library.cpp(59) : error C2679: binary '=' : no operator found which takes a right-hand operand of type 'Book *' (or there is no acceptable conversion)
    \book.h(27): could be 'Book &Book:perator =(const Book &)'
    while trying to match the argument list '(Book, Book *)'

    From what I've read about this error, I seems to be related to incorrect variables sent to the constructors, am I calling the 'holding' and 'book' constructors correctly?

    Here is the related code.
    In main():
    Code:
    cout << "Title: ";
    std::getline(std::cin, titleTemp);				
    cout << endl << "Author: ";
    std::getline(std::cin, authorTemp);
    cout << endl << "CallNum: ";
    cin >> callNumTemp;
    
    Line 59: myBook[x] = new Book(titleTemp,authorTemp,callNumTemp);
    In book.h and book.cpp
    Code:
    class Book : public Holding {
          
    protected:
    	std::string author;
    	int authorLength;
    
    public:
    	Book(std::string titleTemp, std::string authorTemp, int callNumTemp);
    	~Book();};
    
    ***cpp***
    Book::Book(std::string title, std::string tempAuthor, int callNumtemp) :  Holding(title, *callNumber) {
    
    	author = tempAuthor;}
    In holding.h and holding.cpp

    Code:
    class Holding{
          
    protected:
    	std::string title;
    	int titleLength;  
    	int *callNumber;
    
    public:
         Holding(/*char**/std::string titleTemp, int callNumTemp);
    	 Holding();
    
    ****cpp***
    Holding::Holding(std::string tempTitle, int callNumTemp){                     
    	title = tempTitle;
    	*callNumber = callNumTemp;
    }
    Thanks again, Anon, Elysia, and tabstop for your help.

  13. #13
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    new returns a pointer to a freshly constructed Book. You don't want that, since you have a perfectly good Book already hanging out there on the left side (I think -- I somehow missed how you defined your array there). Do you know how many Books there are supposed to be ahead of time? (If not, you could look into vector.) As it stands, you would presumably want to have some functions that could set the author, call number, etc. of a previously existing Book.

  14. #14
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Well the assignment says that you need pointers and new since you will be creating two different types of Holdings. It doesn't say though that the array of pointers to holdings should be declared as global in a separate header. More likely you should just declare it in main() and getInfo should just return a new Holding (that is Book/Record) that you store in that array.

    There is also no need for the global myBook since it is not used for anything that has to live beyond a function call (and strangely I see you are also calling the destructor on the object through that that you have just allocated - don't do that)

    I don't see how using char* instead of string helps with the task at hand which is learning polymorphism. There is nothing polymorphic about char*, just an awful lot of tedious and repetitious work needed for anything (unless you write your own String class - but why should you?)

    You don't need a destructor in Holding to release char arrays, you need it because Holding is an abstract base class and you need a destructor that can be declared virtual. The body can be completely empty, but without this the destructor of Book won't get called if you later delete a ptrHolding and its members - again even if they are std::string's - won't be destroyed.
    Last edited by anon; 12-08-2008 at 02:00 AM.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  15. #15
    Registered User
    Join Date
    Dec 2008
    Posts
    13

    Smile

    Before I continue coding, am I on the right track now?

    I'm learning so much here, what a find.

    Code:
    // library.cpp
    
    #include <sstream>
    #include <iostream>
    #include <fstream>
    #include <stdlib.h>
    #include "library.h"
    #include <string>
    Book *getInfo();
    using namespace std;
    ofstream csis;
    
    int main(){
        csis.open("csis.dat");
    	
    	Holding *holdPtr[5];
    	
    	for (int i=0;i<5;i++){
    		/*holdPrt[i] = new Holding(); can't do that */
    		*holdPrt[i] = getInfo();  //need to figure out how to declare holdPrt
    	}
    	for (int i=0;i<5;i++){
    		holdPtr[i]->print();
    	}
        csis.close();  
    }
    
    Book *getInfo(){
        int callNumTemp;
    	char choice;
    	std::string titleTemp;
    	std::string authorTemp;
    	cout << "Enter holdings type: " << endl;
    	cout << "/'b/' for book and /'r/' for recordings: ";
    	cin >> choice;
    
    	if (choice == 'b' || choice == 'B') {
    		cout << "Title: ";
    
    		std::getline(std::cin, titleTemp);
    			
    		cout << endl << "Author: ";
    		std::getline(std::cin, authorTemp);
    
    		cout << endl << "CallNum: ";
    		cin >> callNumTemp;
    		Book *myBook;
    		myBook = new Book(titleTemp,authorTemp,callNumTemp);
    		return myBook;
    	} else 
    		cout << "else called (testing)"<< endl;
    return 0;
    	
    }
    Last edited by Skyy; 12-08-2008 at 08:57 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. template and polymorphism
    By lehe in forum C++ Programming
    Replies: 7
    Last Post: 06-22-2009, 02:41 PM
  2. Base-class pointer, accessing object from derived class
    By Korhedron in forum C++ Programming
    Replies: 15
    Last Post: 09-28-2008, 05:30 AM
  3. Smart pointer class
    By Elysia in forum C++ Programming
    Replies: 63
    Last Post: 11-03-2007, 07:05 AM
  4. Replies: 3
    Last Post: 10-31-2005, 12:05 PM
  5. polymorphism
    By slaveofthenet in forum C++ Programming
    Replies: 15
    Last Post: 07-10-2003, 11:50 AM

Tags for this Thread