// trace.h - class definitions for tracing class #ifndef traceh_ #define traceh_ class Trace { public: Trace(char *); Trace(){fn=0;}; ~Trace(); friend void TraceOn(); friend void TraceOff(); private: static bool Trace_flag; char *fn; }; #endif //traceh_The static value gets initialized only once, in the definition file.
// trace.cc - tracing class functions #include <stdlib.h> #include <stdio.h> #include <iostream.h> #include <string.h> #include "trace.h" bool Trace::Trace_flag=false; Trace::Trace(char * name) { cerr << "T:entering <" << name << ">" << endl; fn=strdup(name); } // Trace constructor Trace::~Trace() { if(fn != 0) { cerr << "T:leaving <" << fn << ">" << endl; free(fn); } } // Trace destructor void TraceOn() { Trace::Trace_flag=true; } // TraceOn void TraceOff() { Trace::Trace_flag=false; } // TraceOFFThe friend functions are needed to alter the private data. And so you don't need to create an instance just to set the data.
All C and C++ args are passed by value. However, C++ has added pass by reference. It is recommended to use this for almost all parameters as it can speed things up.
int foo(int a) // (A) int foo(int& a) // (B)In (A), since a copy of a is being made when the function is called, the constructor must be called. And when it exits, the destructor must be called. The effect of pass by value (no alteration of the original) can be achieved by
int foo(const int& a) // (C)This allows the compiler to push just an address on the stack and prevents the function from altering it.
When passing an object as a parameter and doing it by value, the bits of the object are passed from the callers environment to the called functions environment. The local var is a bit copy of the original. This isn't always desirable. One example is that it copies the state of static members and you might not want this. So you must write a special constructor called a copy constructor to handle this. You add a member function that looks like:
mycc(const myclass&);This will be called any time a class instance is passed by value. Examples include:
foo(myclass a) {}; myclass x; x= bar(z); // copies return value to x
class string { public: char * operator+(const string str) { char *result= new[this->length() + str.length() + 1]; strcpy(result,this->value); strcat(result,str.value); return(result); } private: char value[32]; }
You can create your own insertors by overloading the << operator. To print a name with a label for a class you created:
ostream & operator<<(ostream& os, const myclass &t) { os << "myclass:name is " << t.name << endl; return os; } // << operator
It is possible to overload the ++ and -- operators. You can even differentiate between pre and post increment. The compiler ass the prefix call,
++a;as a call to
operator++(a)However, a postfix call is seen differently:
a++; operator++(a,int)The compiler provides the dummy second argument.
Virtual functions are a method of providing that the overridden versions of a base class method get executed. Often the base class method is instantiated so there is always something to run. However, you may want to build the base class as an abstract class. This would have the interface and the data, but no function. All the methods are virtual functions and they have no code. These are defined by setting them to 0.
virtual void foo() const = 0;The function is defined by cannot be executed. It must be overridden by the derived class.