Jump to content

C++ moving classes away to separate files

WildCAt
Go to solution Solved by Yamoto42,

I will use the deck class as an example:

 

In a  file with ".h" extension.  typically you name this after the class it contains, but that is just a strong recommendation, not a requirement.  This is then #included in your other files where the class is needed.  for custom header files, use #include "myfile.h", not #include <myfile>.  the quotation marks tell it to look in a directory relative to the file it is being included to instead of the system defined locations.

#ifndef _DECK_H
#define _DECK_H

class deck{

	private:

	char types[4]={'H','S','D','C'};
	char numbers[14]={'1','2','3','4','5','6','7','8','9','0','J','Q','K','A'};

	char used_types[50];
	char used_numbers[50];
	int used_quantity=0;
	
	public:
		void give_card(int *ok, int x, char *type,char *number);

}

#endif

 

 

And in a .cpp file, define the function bodies.  Again, name doesn't actually matter, but it's easier to remember what it is if you use the class name.

void deck::give_card(int *ok, int x, char *type,char *number){
	int seed=1;
	    //if (*ok==0)seed=55;
	    srand(time(0)/seed);  <== remove this.   this only needs to be called once EVER
	    string combine;
	    char r_type=types[rand()%3];
	    char r_number=numbers[rand()%14];
	    for(int i=0;i<used_quantity;i++){
		    if(r_type==used_types[i] && r_number==used_numbers[i]){*ok=0; break;}
		    else *ok=1;
	    }
	if(*ok!=0){
		used_types[used_quantity]=r_type;
		used_numbers[used_quantity]=r_number;
		used_quantity++;

	    type[x]=r_type;
	    number[x]=r_number;
	}
}

Only code things i will note are the re-seeding of the RNG, which is bad, and declaring global variables, which while sometimes necessary is frowned upon.  Seed the RNG as one of the first things you do in main(), then just leave it.

 

Each file (including the .h files) do need to have their own #includes.  Similarly, the #ifndef, #define, and #endif in the header file prevent infinite re-inclusion, as things may only be defined once.  #pragma once as the first line in any .h file MAY also accomplish this similarly, but it is non-standard and not 100% guaranteed to be supported.  That 1% of systems that don't support something will always be the ones you end up having to use...


To compile, just add the extra .cpp files to the command line.

Hello! ^^

I'm studying c++ and I could not find much info on how to separate classes in separate file... I mean, there is a lot of examples, but they are just basics, nothing complex at all.

I have a program with everything written in single cpp and now I'd like to move classes away from it.

Can you please make an example based on my code? I just need to understand how it works in more complex code.

Here is the entire code of my program in spoiler below.

Spoiler

	#include <iostream>
	#include <random>
	#include <ctime>
	#include <stdio.h>
	


	using namespace std;

	
	void cls(){
	cout << "\033[2J\033[1;1H";
	//ANSI escape codes.  (\033[2J) valo ekrana  (\033[1;1H) nustato kursori ant 1 stulpelio 1 eilutes
	}
	

	//////////////////////////
	/////////DECK////////////
	////////////////////////


	class deck{

	private:

	char types[4]={'H','S','D','C'};
	char numbers[14]={'1','2','3','4','5','6','7','8','9','0','J','Q','K','A'};

	char used_types[50];
	char used_numbers[50];
	int used_quantity=0;
	public:



	void give_card(int *ok, int x, char *type,char *number){
	    int seed=1;
	    //if (*ok==0)seed=55;
	    srand(time(0)/seed);
	    string combine;
	    char r_type=types[rand()%3];
	    char r_number=numbers[rand()%14];
	    for(int i=0;i<used_quantity;i++){
		    if(r_type==used_types[i] && r_number==used_numbers[i]){*ok=0; break;}
		    else *ok=1;
	    }
	if(*ok!=0){used_types[used_quantity]=r_type;
	used_numbers[used_quantity]=r_number;
	used_quantity++;

	    type[x]=r_type;
	    number[x]=r_number;
	}
	}


	}deck1;



	//////////////////////////
	/////////PLAYER//////////
	////////////////////////

	class player{
	private:

	    char type[5];
	    char number[5];
	    int n=0;
	    int chips=100;

	public:


	    int card_quantity(){
		return n;
	    }


	    void player_take(){
		int ok=1;
		start_player:
		    deck1.give_card(&ok, n, type, number);
		    if(ok==0) {goto start_player;}
		    n++;
		}

	char return_type(int koki){
	return type[koki];
	}

	char return_number(int koki){
	return number[koki];
	}

		void show_cards(){

		    cout<<"Player cards: "<<endl;
		    for(int i=0;i<n;i++){
		        cout<<type[i];
		        cout<<number[i]<<endl;
		    }


		}

	    int sum(){
		    int total=0;
		    for(int i=0;i<n;i++){
		    if(number[i]=='1') {total+=1;}
	       else if(number[i]=='2') {total+=2;}
	       else if(number[i]=='3') {total+=3;}
	       else if(number[i]=='4') {total+=4;}
	       else if(number[i]=='5') {total+=5;}
	       else if(number[i]=='6') {total+=6;}
	       else if(number[i]=='7') {total+=7;}
	       else if(number[i]=='8') {total+=8;}
	       else if(number[i]=='9') {total+=9;}
	       else if(number[i]=='0') {total+=10;}
	       else if(number[i]=='J') {total+=10;}
	       else if(number[i]=='Q') {total+=10;}
	       else if(number[i]=='K') {total+=10;}
		        }

		    for(int i=0;i<n;i++){
	       if(number[i]=='A') {if(total+11<=21)total+=11; else total+=1;}
	}
		    return total;
		}


	int show_chips(){
	return chips;
	}

	void take_chips_from_player(int chips_amount){
	chips-=chips_amount;
	}

	void give_chips_to_player(int chips_amount){
	chips+=chips_amount;
	}

	}player1;


	//////////////////////////
	/////////DEALER//////////
	////////////////////////

	class dealer{
	private:
	char type[5];
	char number[5];
	int n=0;

	public:

	    int card_quantity(){
		return n;
	    }

	    void dealer_take(){
		int ok=1;
		start_dealer:
		    deck1.give_card(&ok, n, type, number);
		    if(ok==0) {goto start_dealer;}
		    n++;
		}

		void show_cards(){
		    cout<<"Dealer cards: "<<endl;
		    for(int i=0;i<n;i++){
		        cout<<type[i];
		        cout<<number[i]<<endl;
		    }
		}


		int sum(){
		    int total=0;
		    for(int i=0;i<n;i++){
		    if(number[i]=='1') {total+=1;}
	       else if(number[i]=='2') {total+=2;}
	       else if(number[i]=='3') {total+=3;}
	       else if(number[i]=='4') {total+=4;}
	       else if(number[i]=='5') {total+=5;}
	       else if(number[i]=='6') {total+=6;}
	       else if(number[i]=='7') {total+=7;}
	       else if(number[i]=='8') {total+=8;}
	       else if(number[i]=='9') {total+=9;}
	       else if(number[i]=='0') {total+=10;}
	       else if(number[i]=='J') {total+=10;}
	       else if(number[i]=='Q') {total+=10;}
	       else if(number[i]=='K') {total+=10;}
		        }

		    for(int i=0;i<n;i++){
	       if(number[i]=='A') {if(total+11<21)total+=11; else total+=1;
	//cout<<total;
									}
	}
		    return total;
		}


	char return_type(int koki){
	return type[koki];
	}

	char return_number(int koki){
	return number[koki];
	}

	    void dealer_ai(){
		            ;
		                //The dealer must hit if the value of the hand is lower than 17

	    }


	}dealer1;



	//////////////////////////
	/////////MAIN////////////
	////////////////////////














	class draw_interface{


	public:

	void window_line(){
	cout<<"********************************************************************        \n";
	}
	void window_left(){
	cout<<"* ";
	cout<<" ";
	}

	void card_line(){
	cout<<"*****";
	cout<<" ";
	}
	void card_2(char type){
	cout<<"*"<<type<<"  *";
	cout<<" ";
	}
	void card_3(char number){
	if(number=='0') cout<<"*"<<10<<" *";
	else cout<<"* "<<number<<" *";
	cout<<" ";
	}
	void card_4(char type){
	cout<<"*  "<<type<<"*";
	cout<<" ";
	}
	void endline(){
	cout<<endl;
	}

	void draw_ui(int kiek_piesti, string who, bool onlyfirst){
	//for(int i=0;i<kiek_piesti;i++){
	//window_left();endline();
	//window_left();endline();
	//window_left();endline();
	window_left();
	for(int i=0;i<kiek_piesti;i++){
	card_line();
	}
	endline();
	window_left();
	for(int i=0;i<kiek_piesti;i++){
	if(who=="player")card_2(player1.return_type(i));
	else if (onlyfirst && who=="dealer" && i>0) card_2('#');
	else card_2(dealer1.return_type(i));
	}
	endline();
	window_left();

	for(int i=0;i<kiek_piesti;i++){
	if(who=="player")card_3(player1.return_number(i));
	else if (onlyfirst && who=="dealer" && i>0) card_3('#');
	else card_3(dealer1.return_number(i));
	}
	endline();
	window_left();
	for(int i=0;i<kiek_piesti;i++){
	if(who=="player")card_4(player1.return_type(i));
	else if (onlyfirst && who=="dealer" && i>0) card_4('#');
	else card_4(dealer1.return_type(i));
	}
	endline();
	window_left();
	for(int i=0;i<kiek_piesti;i++){
	card_line();
	}


	endline();
	/*
	window_left();
	endline();
	window_left();
	endline();
	window_left();
	endline();
	window_left();
	endline();
	window_line();
	*/


	}


	}ui;




	void menu(){

	    cout<<"1.Hit"<<endl;
	    cout<<"2.Stand"<<endl;
	    cout<<"3.Double"<<endl;
	    cout<<"4.Split?"<<endl;
	    cout<<"5.End game"<<endl;

		int choice;
	cin>>choice;

		 switch(choice) {
		   case 1:
	cls();
		     //cout << "Hit";
		     player1.player_take();
	//             player1.show_cards();
	ui.draw_ui(dealer1.card_quantity(), "dealer", true);
	ui.draw_ui(player1.card_quantity(), "player", false);
		     cout<<"<<<<<<<<<<<<<<"<<player1.sum()<<">>>>>>>>>>>>>>"<<endl;
		     if (player1.sum()>21){
	cls();
	ui.draw_ui(dealer1.card_quantity(), "dealer", false);
	ui.draw_ui(player1.card_quantity(), "player", false);
	cout<<"<<<<<<<<<<<<<<Busted!>>>>>>>>>>>>>>"<<endl;
							break;}
		     menu();
		     break;



		   case 2:
	cls();
	while (dealer1.sum()<17){dealer1.dealer_take();}

	//dealer1.show_cards();
	//player1.show_cards();
	ui.draw_ui(dealer1.card_quantity(), "dealer", false);
	ui.draw_ui(player1.card_quantity(), "player", false);
	cout<<endl<<endl<<"Player: "<<player1.sum()<<" ";
	cout<<endl<<"Dealer: "<<dealer1.sum()<<" ";
			if(player1.sum()>dealer1.sum() && dealer1.sum()<=21){cout<<"You Win!"<<endl;} 
			else if (player1.sum()<=21 && dealer1.sum()>21){cout<<"You Win!"<<endl;} 
			else if(player1.sum()==dealer1.sum()){cout<<"Push!"<<endl;}		
	else cout<<"You lose!"<<endl;
	break;
		     menu();
		     break;





		   case 3:
	cls();

		     cout << "Double";
		     menu();
		     break;




		   case 4:
	cls();

		     cout << "Split?";
		     menu();
		     break;






		   case 5:
	cls();

		     cout << "End game";
	cls();

		     break;
		   default:
	cls();
	ui.draw_ui(dealer1.card_quantity(), "dealer", true);
	ui.draw_ui(player1.card_quantity(), "player", false);
			menu();
		 }

	}




	void ai(){
	    while (dealer1.card_quantity()<2){
		dealer1.dealer_take();
	    }

	//    dealer1.show_cards();
	    while (player1.card_quantity()<2){
		player1.player_take();
	    }
	//    player1.show_cards();


	ui.draw_ui(dealer1.card_quantity(), "dealer", true);
	ui.draw_ui(player1.card_quantity(), "player", false);


	    menu();
	}




	int main()
	{

	cls();



		                                        ai();






	    return 0;
	}

 

I know my code is garbage but hey, I'm just learning here. It's just for me to better understand how everything works. Also some things here may be "linux only".

It would be great if you could show me how to move class deck into deck.h and deck.cpp files. I would really appreciate that! I just need an example to move on :(

Link to comment
Share on other sites

Link to post
Share on other sites

1) Invest in Clang Format. It's a beautiful little code formatting tool for any language.

2) I don't see what's difficult about this that basic tutorials don't cover. You can literally dump deck into deck.cpp and then use a basic header tutorial to just put the class and function declarations inside deck.h.

Then just make sure deck.cpp has #include "deck.h", and any files using deck have #include "deck.h"

 

 

Software Engineer for Suncorp (Australia), Computer Tech Enthusiast, Miami University Graduate, Nerd

Link to comment
Share on other sites

Link to post
Share on other sites

I don't really want to touch your code, I would need to fix indentation, but the rule is simple, int your header file you leave only function declaration, and you move definition to cpp file. Next you add header guard to header file. For example:

#include <iostream>

class Foo {
  
  private:
    int i;
    
  public:
    void bar(int _i){
      i = _i;
    }
  
} foo;

int main() {
    foo.bar(10);
}

Header file would look like:

#ifndef FOO_H
#define FOO_H

class Foo {
  
  private:
    int i;
    
  public:
    void bar(int _i);
  
};

#endif

and cpp:

#include "Foo.h"

void Foo::bar(int _i){
  i = _i;
}

and main:

#include <iostream>

#include "Foo.h"

int main() {
  Foo foo;
  foo.bar(10);
}

 

If your cpp needs somthing to be included then you includ eit in cpp file, it's better this way then including it in header file. If you will have two classes, A and B, A will require B and B will require A then you can add class A; in B.h and class B; in A.h, It may be vague right now., but if you ever will run into such problem then you will probably learn by figuring this out.

Link to comment
Share on other sites

Link to post
Share on other sites

10 minutes ago, Mr_KoKa said:

I don't really want to touch your code, I would need to fix indentation, but the rule is simple, int your header file you leave only function declaration, and you move definition to cpp file. Next you add header guard to header file. For example:

 

If your cpp needs somthing to be included then you includ eit in cpp file, it's better this way then including it in header file. If you will have two classes, A and B, A will require B and B will require A then you can add class A; in B.h and class B; in A.h, It may be vague right now., but if you ever will run into such problem then you will probably learn by figuring this out.

Seriously man, symbolically named classes (window 1) for a newbie? Don't do that, even as an expert. It's nothing but code obfuscation. Otherwise, nicely done.

Software Engineer for Suncorp (Australia), Computer Tech Enthusiast, Miami University Graduate, Nerd

Link to comment
Share on other sites

Link to post
Share on other sites

I will use the deck class as an example:

 

In a  file with ".h" extension.  typically you name this after the class it contains, but that is just a strong recommendation, not a requirement.  This is then #included in your other files where the class is needed.  for custom header files, use #include "myfile.h", not #include <myfile>.  the quotation marks tell it to look in a directory relative to the file it is being included to instead of the system defined locations.

#ifndef _DECK_H
#define _DECK_H

class deck{

	private:

	char types[4]={'H','S','D','C'};
	char numbers[14]={'1','2','3','4','5','6','7','8','9','0','J','Q','K','A'};

	char used_types[50];
	char used_numbers[50];
	int used_quantity=0;
	
	public:
		void give_card(int *ok, int x, char *type,char *number);

}

#endif

 

 

And in a .cpp file, define the function bodies.  Again, name doesn't actually matter, but it's easier to remember what it is if you use the class name.

void deck::give_card(int *ok, int x, char *type,char *number){
	int seed=1;
	    //if (*ok==0)seed=55;
	    srand(time(0)/seed);  <== remove this.   this only needs to be called once EVER
	    string combine;
	    char r_type=types[rand()%3];
	    char r_number=numbers[rand()%14];
	    for(int i=0;i<used_quantity;i++){
		    if(r_type==used_types[i] && r_number==used_numbers[i]){*ok=0; break;}
		    else *ok=1;
	    }
	if(*ok!=0){
		used_types[used_quantity]=r_type;
		used_numbers[used_quantity]=r_number;
		used_quantity++;

	    type[x]=r_type;
	    number[x]=r_number;
	}
}

Only code things i will note are the re-seeding of the RNG, which is bad, and declaring global variables, which while sometimes necessary is frowned upon.  Seed the RNG as one of the first things you do in main(), then just leave it.

 

Each file (including the .h files) do need to have their own #includes.  Similarly, the #ifndef, #define, and #endif in the header file prevent infinite re-inclusion, as things may only be defined once.  #pragma once as the first line in any .h file MAY also accomplish this similarly, but it is non-standard and not 100% guaranteed to be supported.  That 1% of systems that don't support something will always be the ones you end up having to use...


To compile, just add the extra .cpp files to the command line.

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×