New to C++ Classes design

Hi guys! Thanks for moderating such an informative forum...this has become my best resource for c++ info!

I'm having what should be a familiar problem to any C++ classes newb. My questions revolve around the over all scoping of C++ classes. Maybe I've been at this for too long and have tried too many things and and am complicating things as usual.

I was hoping someone could think about my problem from the best practices standpoint... I'm so tired of being taught bad practices and have little confidence when it comes to how to go about doing something when there are SO many ways to do it!

What I'm trying to do is this: Write a tokenizer aka scanner program for a text file containing source data.

Basically I need to be able to suck in a text file and then use char/string manipulation (which I know I can do) so that the next successive token is returned when calling the scanner.

But I'm not even concerned with that part of it.

Before I program I try to get my structures working and my functions to just return. But I'm confused by the wealth of information available here and think im using concepts designed for different goals.

My Goals:

1- Have a main() that takes the program and then Initializes a Scanner() Class which initializes a TokenList() containing a vector of Token() objects.

2- Have scanner do the work, calling members of Tokenlist which fill the data from the Token Class.

I don't know if this makes any sense to anyone but I visualize it like this.
Main---Scanner::aScanner-----TokenList::tokenVector-----Token::attributes

My Problems/Questions:

1- does the over all template make sense?
2- Am I making this a lot harder than it is?
3- I've tried too many losing combinations to count.
4-What more do I have to do to be able to use a vector<Token> tokenVector?
5- I know I need friends, but do I need to "friend" things?

Please forgive me for the incredibly noob post and thank you guys in advance for any suggestions/advice links/snippits you can provide!

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <fstream>
#include <iostream>
#include "Scanner.h"

using namespace std;

int main ( int argc, char *argv[] ){
	Scanner lizardScanner;

	if ( argc != 2 )
		cout<<"usage: "<< argv[0] <<" <filename>\n";
	else {
		ifstream the_file ( argv[1] );
	if ( !the_file.is_open() )
		cout<<"Could not open file\n";
	else {
		cout<<"About to try creating a lizardScanner"<<endl;
		lizardScanner = new Scanner();
		cout<<"Returned to main after creating new lizardScanner"<<endl;
	
    }

  }
}


Scanner.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <fstream>
#include <iostream>
#include <cctype>
#include "TokenList.h"

class Scanner{

	public:
		Scanner();
	
	private:
		TokenList aTokenList;

};


Scanner.cpp
1
2
3
4
5
6
7
8
9
10
11
12
#include "Scanner.h"

Scanner::Scanner(){
	cout <<"Default Scanner Constructor called"<<endl;
	aTokenList = new TokenList();
	cout << "Created aTokenList in Scanner.c"<<endl;
}

Scanner::Scan() Token{

}


TokenList.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <string>
#include <vector>
#include "Token.h"

//class Token; // Tell the compiler that Token is an internal class

class TokenList{
	
	public:
		TokenList(); //default constructor
		int AddToken(std::string category);
		int AddToken( std::string category, std::string attribute);
		int RemoveLastToken();
//		void ~TokenList();
		
	private:
		Token aToken;
		vector<Token> tokenVector;
		vector<Token>::iterator iter, iter2, beg, end;

}


TokenList.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "TokenList.h"

TokenList::TokenList(){		//must be called once to init aToken
	cout <<"Default TokenList Constructor Called"<<endl;
	aToken = new Token();
	tokenVector.clear();
	iter = tokenVector.begin();
	iter2 = tokenVector.begin();
	beg = tokenVector.begin();
	end = tokenVector.end();
	cout << "Created aToken and tokenVector in TokenList.cpp"<<endl;
}

TokenList::AddToken(string category) int {
	aToken = Token(category);
	tokenVector.push_back(aToken);
	iter++;
	return tokenVector.size();
}

TokenList::AddToken(string category, string attribute) int {
	aToken = Token(category, attribute);
	tokenVector.push_back(aToken);
	iter++;
	return tokenVector.size();
}

TokenList::RemoveLastToken(){
	tokenVector.erase(--iter);
	return tokenVector.size();
}

//TokenList::~TokenList(){
//	tokenList.~vector<Token>();
//}


Token.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <string>

class Token {

	public:
		Token();
		Token(std::string category );
		Token(std::string category, std::string attribute);
		Token(const Token& a);
		Token& operator=(const Token& a);
//		~Token();
		
	private:
		std::string tokenCategory;
		std::string tokenAttribute;
};


Token.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "Token.h"

Token::Token(){
	cout<<"Default Token Constructor Called"<<endl;
	tokenCategory = "defaultCategory";
	tokenAttribute = "defaultAttribute";
	cout<<"Initialized Token Default Values"<<endl;
}

Token::Token(string category){
	tokenCategory = category;
	tokenAttribute = "";
}

Token::Token(string category, string attribute){
	tokenCategory = category;
	tokenAttribute = attribute;
}

Token::Token(const Token& a){
	Token anotherToken = new Token;
	anotherToken->category = a->category;
	anotherToken->attribute = a->category;
}

Token& Token::operator=(const Token& a){
	if(this != &a){
		delete anotherToken;
		Token anotherToken = new Token;
		anotherToken->category = a->category;
		anotherToken->attribute = a->category;
	}
	return *this;
}

//Token::~Token(){} 


If you've gotten this far then I already owe you.

ALL FEEDBACK IS GOOD FEEDBACK. Please don't tell me to give it up. I'm determined!

Respectfully,
-W
Let's get one thing straight: it's <return type> <function name>(<parameter list>) for the declaration and <return type> <namespace>::<function name>(<parameter list>) for the definition.

It seems to me that your Scanner class is somewhat superfluous.

Members 'iter', 'iter2', 'beg', and 'end' are completely redundant.

Line 5@TokenList.cpp: No. new returns a pointer.
Lines 15-16 and 22-23@TokenList.cpp: Invalid syntax and the idea itself is stupid. No offense.
Line 21@Token.cpp: Again.
Line 22-23@Token.cpp: Arg! You aren't actually expecting the compiler to automatically deduce that you want a pointer and not an object, are you?
Line 27@Token.cpp: Wrong syntax.
Line 28@Token.cpp: Undeclared identifier.

There.
You still have a ways to go with the basics. I'd go back and read some more.
Helios:

Thank you for responding! I would be the first agree that I have a ways to go. Almost everything I do is stupid.

I'm a VHDL/ASM/c guy and haven't had the chance to make the sacrifices needed to learn object oriented c++ until now.

Based on your comments and further reading I decided to start over yet again.

I consolidate the scanner into one file after seeing how some other people (Jamsa mostly) do things.

I got rid of the TokenList and am thinking about perhaps changing the Token class to Tokens and adding a token vector to it.

Right now the program prints out the tokens although with no error handling whatsoever to the screen.

What I THINK I need to further my understand with is the copy and assignment constructors especially. As a token is formed in Scanner() I think I want to then push it right into the vector but I'm not sure if I need to make a copy of that to do it or if I need to make a TokenVector class. I guess I don't understand how to write the copy or assignment methods as a method in the same class.

Thanks again Helios for your advice as well as your patience.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//#include "scanner.h"
#include <iostream>
#include <fstream>
#include <string>
#include <cctype>

using namespace std;

class Scanner;
class Token{

	public:
		Token()
			{cout<<"created token"<<endl;
			category = "Default";
			attribute = "Default";
			PrintToken();
			};

		Token(std::string thecategory){
			category = thecategory;
			attribute = "";
			PrintToken();
		};
		Token(std::string thecategory, std::string theattribute){
			category = thecategory;
			attribute = theattribute;
			PrintToken();
		};
		void PrintToken(){
			cout<<"<"<<category<<","<<attribute<<">"<<endl;
		};
		Token(const Token& a){ //copy constructor
		
		};
		Token& operator=(const Token& a){
		
		};		//assignment
		friend class Scanner;
	
	private:
		string category;
		string attribute;

};


//When a token is added it should also be returned?
class TokenList{
	public:
		TokenList()
			{cout<<"Default TokenList constructor"<<endl;
			size = 0;
			ptr = 0;
			};
			
		TokenList(int itsSize){
		
		};	
			
		int listSize() const {return size;};

	private:
//		Token aToken;
		Token aTokenArray[];
		int size;
		int ptr;	
};

class Scanner: public Token { 

	public:
		Scanner();
		Scanner(ifstream &daStream): Token(){
			cout<<"Processing Filestream into Tokens"<<endl<<endl;
			while(daStream.get(x)){
	//			cout<<x<<endl<<endl; 										//Print the file normally
				if( x=='-' && (daStream.peek()=='-')){				//Ignores Comments
					while(x!='\n')
						daStream.get(x);
				}
				if(isalnum(x)){
					tokenText.append(1,x); 			//if its not a space then it belongs in the token
					if(isspace(daStream.peek()) || ispunct(daStream.peek()) || iscntrl(daStream.peek() )){	//peek to see if NEXT char is space, or punct if so then current char is last of token
						Token aToken(tokenText);	//create token regardless of validity
						tokenText.clear();			//clear the stale tokenText info
					}
				}
				if(ispunct(x)){
					tokenText.append(1,x);
					if(isspace(daStream.peek()) || isalnum(daStream.peek())){
						Token someToken(tokenText);
						tokenText.clear();
					}
				}		

			}
	//		daStream.seekg(0);
		};
		Token Scan(){};			//RETURN THE NEXT TOKEN
	
	private:
		char x;
		Token aToken;
		string tokenText;
	//	TokenList theTokenList;
		
};

int main ( int argc, char *argv[] ){

long begin,end;
char x;

	if(argc!=2)
		cout<<"usage: "<< argv[0] <<" <filename>\n";
	else{
		ifstream myfile (argv[1]);
		if(!myfile.is_open())
			cout<<"Error opening file "<<argv[1]<<endl;
		else{
			cout<<"Source Filename is "<<argv[1]<<endl;
			begin = myfile.tellg();
			myfile.seekg (0, ios::end);
			end = myfile.tellg();
			
			cout << (end-begin) << " Bytes"<<endl;
			myfile.seekg(0);
			Scanner lizardScanner(myfile);			
		}
	}
	return 0;
}


Is this any less superfluous and stupid, haha?

Oh.. also... is it common to overwrite the << operator to make a way to print a classes contents?

Thanks again.
I have a couple of comments.

1. By deriving Scanner from Token, you are saying that a Scanner is a
specialized kind of Token. From a high-level reasoning standpoint,
this doesn't make sense to me.
2. You do not need to explicitly define a copy constructor or an
assignment operator since the default one provided to you by the
compiler (ie, member-wise copy unless you are using an old compiler)
will suffice.
3. You should get into the habit of passing non-trivial types by either
non-const reference or const reference. This is mainly applying to
std::strings in your code.
4. You should also get into the habit of using initializer lists in your
constructors as they are more efficient. For example:

1
2
3
4
Token::Token( const std::string& cat ) :
   category( cat ), attribute()
{
}


is more efficient than

1
2
3
4
5
Token::Token( const std::string& cat )
{
   category = cat;
   attribute = "";
}


because in the latter case, first the default constructor is called
for both category and attribute and then the assignment operator.
In the former case, only a constructor is called.

5. I'm not sure you need to declare Scanner a friend of Token because
of the derivation, however as a matter of general rule, friendship
often is a sign of improper design.

6. Yes, it is reasonable and common to write operator<< to output
contents of classes. I do it all the time, if only for debugging purposes.

Topic archived. No new replies allowed.