Classes
Classes are
similar to structs with the exception of a few things.
1) classes can have
member functions.
2) they support
public and private data
Example:
class myclass
{
private:
//
by default all class members are private
int x,y;
char c;
int func(void);
Mytype acct; // you can
also have a member of another
//
type that you created.
public:
void setx(int x);
myclass()
// default constructor to
initialize
~myclass()
// destructor to clean up out of
scope
};
in the main you
can code the following:
#include <iostream>
int
main()
{
myclass c;
c.setx(10); // can do because member function is
public
return 0;
}
You can't code
c.x = 10; because x is a private
variable.
…use get and set
to retrieve and set values.
class Target
{
int x,y;
bool found;
};
Then in main to
declare you would code…
Target p[3]; //declares an array of
Targets
to get at the
information you do similar to a struct:
p[2].x
p[2].y
p[2].found = true;
More
Examples:
class T
{
public:
int x,y;
double f;
private:
int j,k;
};
in main(
)…
T
a,b; // variables - objects - instances of type
T
a.y
= b.x; // OK
a.f
= b.y; // OK
a.f
= b.j; // Can't because j is private
a.j
= b.j; // Can't "
"
**One
helpful thing in C++ classes is that you can do direct assignment of class
varialbes without worrying about copying each one.
i.e. a = b; // copies all elements of
a into b
This is an easy
way to copy arrays in one fall swoop.
Class
Member Functions:
They only apply
to the class that they are in.
any member
function has access to all of the members of the class so when you are outside
you can use a function to get a value when you cannot access the data members
directly. This is where a get and set function come into play.
Member
Function Headers:
int
T::get_j(void)
{
return j; // sends current instance of
j
}
first you have
int as a return type
T distinguishes
the function as a member of class T
get_j is the
method name
when you call
this function you use a.get_j() and it will call
the function and return the value of j for the
instance a of the class T
void T::set_j(int I)
{
if(I<0) // lets you set positive values
only
return;
j = I:
}
later on in the
code you call…
a.set_j(3);
// lets you set private value of j to value of 3 because
of design of class
Constructors:
You always use a constructor to initialize variables.
Even when you don't code it in, a default constructor is added. They
always have the same name as class.
Example:
class M
{
int x,y;
public:
M(); //
default constructor
M(int) // constructor
for different purpose
};
M::M( )
{
x = 7; // can do
anything inside
y = 3; // now everytime
the program calls class M
}
//
x will be set to 7 and y set to 3
When
calling..
M
t; // x and y
members of M are given 7 and 3 respectively.
M::M(int j)
{
x = 7;
y = j;
}
One thing to be
aware of is if you, for instance, have x in function header…
M::M(int x)
{
x = 7;
y = x;
}
So now with two
constructors, there are multiple ways to call the constructors.
M
a; //
Calls default constructor
M
b(5); // Calls second
constructor
M
c=5; // also calls
second constructor
Sample
Code:
#include <iostream>
class A
{
int x;
public:
A();
// constructor is always
public
A(int);
int get_x(void);
void increment(void);
};
Sample
code continued:
A::A()
{
x = 0;
}
A::A(int s)
{
x = s;
}
int
A::get_x(void)
{
return x;
}
void A::increment(void)
{
x++;
}
int
main()
{
A a;
A b(5);
cout << a.get_x(x) << endl;
b.increment();
cout << b.get_x( ) << endl;
return (0);
}
Breaking
Up A Program:
To make your
code manageable you should break it up into several files and then link them
together when you compile. This will eliminate the problem of having to
change something in the source code and then having to re-compile the entire
project. This seems like small potatoes now but when the projects take
hours to compile you will understand the importance more.
From now on in
this class, each class should have it's own .C
and .h files which will be linked to source
code.
Now in another
file named A.C…
#include "A.h"
A::A( ) // default
constructor
{
code to initialize
}
A::A(int x) // initialize for special
use
{
special initialization
}
void A::print(arguments)
{
printing code
}
same follows for
B.C file for all B class member functions.
so now you will
have
test.C
A.C
B.C
A.h
B.h
all together
they make test.o, A.o, and B.o which are
linked to test, your executable
This can get
rather sticky so use your Makefile to avoid errors…
CC
= g++
CCFLAGS = -Wall -O
test : test.o A.o B.o
$(CC) $(CCFLAGS) -o test\
test.o A.o B.o -lm(if needed
to link math)
test.o : test.C A.h B.h
$(CC) $(CCFLAGS) -c test.C
A.o
: A.C A.h
$(CC) $(CCFLAGS) -c A.C
B.o
: B.C B.h A.h
$(CC) $(CCFLAGS) -c B.C
clean:
-rm *.o test
note the \ in
the first section… This is a way to extend the line in a Makefile. In
order to implement this, you have to put a <cr> directly
following the \.
Reading
Files:
if you want to
access a binary data file from the command line prompt you would include the
following:
#include <fstream>
…
ifstream myfile;
myfile.open("filename", ios::in|ios::binary);
then at the
command prompt you would type:
executable information.dat
when you write
your main function it would look something like this…
int main(int
argc, char * argv[ ])
argc returns the number of arguments(2) while
argv[ ] returns information.dat in
this case
to test if two
arguments were entered you can code:
if
(argc != 2)
cout << "Error please
include data file" << endl;
myfile.open(argv[1], ios::in|ios::binary)
if
(!myfile)
cout << "Error opening
file" << end;
Debugging:
make > errs
redirects output to filename
make >&
errs redirects std. output and std.
errs
Segmentation Fault:
This most often
happens when you are trying to write outside the bounds of an array or pointer
that wasn't properly set.
Core
dump:
When your
program fails when running, all information about your program is placed in a
core file.
cerr
<<:
remember to use
the old faithful standby of print statements to check where you are in
code.
Gnuu:
This is the
debugger we can use in this system. To use it you must include the -g
option in your makefile g++ lines.
g++
-Wall -c -g test.C
g++
-o test -g test.o
A good way to do
this is to make it part of the CCFLAGS statement
but you must ALWAYS remove the -g before
submitting assignment for grading.
at prompt type
gdb… Example: mp% gdb
assign4<cr>
the prompt will
then change from mp% to (gdb)
gdb
options:
r
-(or run)will run debugger
bt
-(backtrace) use u for up and d for down (gives list of
all functions called in reverse order)
l
-(list) lists code in stack with line numbers where you
currently are ("l" again gives you next 10)
to run line by
line:
b 35
-(break) sets a break at line 25
n
-(or next) will step over and execute following line.
every <cr> will execute another line
s
-(or step) will step into a function
p
-(or print) ie. p n.targets will print value in variable
n.targets
c
-(continue) after the break point
delete 1
-will delete breakpoint #1
quit
-will quit the debugger
you can even change
values while running debugger.
Example:
print grid [0][3]<cr>
// prints out what's
there
print grid [0][3] = `J'<cr> // changes
value of variable
Sample
Code:
following is an
example of the .h and .C files to make a class of complex numbers and to
implement them in a source code. This should help to make things
clearer.
Complex Class:
in
filename: complex.h
#ifndef COMPLEX_H
#define COMPLEX_H
class complex
{
double re, im; //
representing real and imaginary
public:
complex();
complex(double r);
complex(double r, double i);
};
#endif /* COMPLEX_H
*/
in
filename: complex.C
#include "complex.h"
complex::complex()
{
re = im = 0.0; //
initialize all to 0
}
complex::complex(double r)
{
im = 0.0;
re = r;
}
complex::complex(double r, double i)
{
re = r;
im = i;
}
in
filename: test.C
#include <iostream>
#include "complex.h"
.
.
.
int
main()
{
complex a; //
calls default constructor
complex b= -3.7; // calls second
constructor
complex c(2.5, 3.7) // calls third constructor
.
.
.
return (0);
}
default
arguments:
in C++ class
definitions you can just write one constructor that will take the place of all
three.
example:
complex(double r=0.0, double i=0.0);
now you can call
the same way…
complex a;
complex b= -3.7;
complex c(2.5, 3.7)
now all will
call the same constructor using the default arguments given.
recall:
in main
code:
get(char *p, int n);
get(char *p, int n, char term);
in header for
class:
get(char *p, int n, char term = `\n');
this is an
example of what we already do that uses default arguments. If you specify
a terminating character or if you don't it will work either
way.
this:
this is a keyword in C++ which refers to the current
instance
How do we use
this?
complex a(1,1), b(2,-1);
complex c;
c =
a + b; // this would be nice…
Overloading operators:
in C++ you can
overload operators as well. In example, we can write code to add two
things we have created.. in the class..
complex operator+ (complex b);
(const
complex &)const; //const because
constant instance
in a member
method:
complex complex::operator+ (complex b);
{
complex result;
result.re = re + b.re;
result.im = im + b.im;
return result;
}
now you can just
add the types together.
in regular
function:
complex operator+ (const complex &a, const complex
&b)
{
complex result;
result.re = a.re + b.re;
// must pass each value
result.im = a.im + b.im;
// assuming they are public
return result;
}
in member
function:
void complex::print(void)
{
cout << re << " " <<
im;
}
overloading the << operator
ostream & operator << (ostream &ostr, const complex
&c)
{
ostr << c.re;
ostr << " ";
ostr << c.im;
return ostream;
}
inside our
complex class… we have data members to public methods in public: that mention
other functions.
friend:
friend says that
here is a function that is not part of this class but is safe and allowed to
access private parts of this class. Friend is used only
inside the class definition.
class complex // revisited
{
public:
friend ostream & operator <<(ostream
&ostr, const complex &);
};
class
declaration:
ie. class ostream; can only be
used when class is a reference. For example…
ostream & operator
<< (ostream & ostr…)
|