This introduction to C++ is provided primarily for C programmers who need to learn just enough C++ to use the RCS Library.
Classes in C++ define types of data structures and the functions that operate on those data structures. Instances of the class are called objects. While the variables and functions in the definition of the class are called members.
Example: c++_ex1.cc
#include <stdio.h> class CLASS_A { public:// Defines the access for the following members. int first_data_member; float second_data_member; void function_member(); }; void CLASS_A::function_member() { printf("first_member = %d, second_member = %lf\n", first_member, second_member); } CLASS_A first_instance_of_A; CLASS_A second_instance_of_A; main() { first_instance_of_A.first_member = 123; first_instance_of_A.second_member = 3.14; second_instance_of_A.first_member = 501; second_instance_of_A.second_member = 2.718; first_instance_of_A.function_member(); second_instance_of_A.function_member(); }
This example produces the following output after being compiled and run.
first_member = 123, second_member = 3.140000 first_member = 501, second_member = 2.718000
The first_instance_of_A and the second_instance_of_A are objects. Both objects of the same class contain the same type of data and provide the same functions but the value of the data for different objects can be different. The keyword public is used to set access permissions so that the members following it can be used anywhere that an object of CLASS_A can be used. The remainder of this document uses only public members. For information on private and protected member access see the references at the end of this document.
Constuctors are functions which are called whenever a new instance of a class is created. They are often used to initialize the data in the class. In NML, constructors allocate memory, initialize communications channels, read configuration files, set the type and size of messages, and perform almost all of the run-time setup necessary for NML to work. There are several times when instances of a class can be created.
Constructors are declared as member functions with the same name as the class. Constructors may also have arguments. If a constructor has arguments, then the arguments should be added to the declaration of any object of that class and when the new operator is used.
Example: c++_ex2.cc
#include <stdio.h> class CLASS_B { public: CLASS_B(char *name); // This is the constructor declaration. }; CLASS_B::CLASS_B(char *name) { printf("Constructor for CLASS_B called with name = %s.\n",name); } // VxWorks users should probably not // declare objects with constructors here. CLASS_B outside_B("outside"); void my_function(); main() { CLASS_B main_B("main"); my_function(); } void my_function() { CLASS_B my_function_B("my_function"); CLASS_B *ptr_to_B; ptr_to_B = new CLASS_B("new"); delete ptr_to_B; // See Destructors }
This example produces the following output after being compiled and run.
Constructor for CLASS_B called with name = outside. Constructor for CLASS_B called with name = main. Constructor for CLASS_B called with name = my_function. Constructor for CLASS_B called with name = new.
VxWorks users should not declare objects with constructors outside of functions, as outside_B is declared unless they use a special C++ loader that calls the constructors of objects declared outside of functions. The default "ld" loader does not call the constructor in this case, which means the object may not be properly initialized.
Destructors are functions that are called whenever an object of the class is destroyed. Destructors are often used to deallocate memory, close communications channels and files, and to return the system to some known or stable state. Objects may be destroyed after the function they were declared in returns, when the delete operator is used or when the program is over. Destructors are declared with the same name as the class except that they are preceded with a "~". Destructors can not have arguments.
Example: c++_ex3.cc
#include <stdio.h> class CLASS_C { public: CLASS_C(char *_name); // This is the constructor declaration. ~CLASS_C(); // This is the destructor declaration. char *name; }; CLASS_C::CLASS_C(char *_name) { name = _name; printf("Constructor for CLASS_C called with name = %s.\n",name); } CLASS_C::~CLASS_C() { printf("Destructor for CLASS_C called with name = %s.\n",name); } // VxWorks users should probably not // declare objects with constructors here. CLASS_C outside_C("outside"); void my_function(); main() { CLASS_C main_C("main"); my_function(); //exit(0); } void my_function() { CLASS_C my_function_C("my_function"); CLASS_C *ptr_to_C; ptr_to_C = new CLASS_C("new"); delete ptr_to_C; // See Destructors }
This example produces the following output after being compiled and run.
Constructor for CLASS_C called with name = outside. Constructor for CLASS_C called with name = main. Constructor for CLASS_C called with name = my_function. Constructor for CLASS_C called with name = new. Destructor for CLASS_C called with name = new. Destructor for CLASS_C called with name = my_function. Destructor for CLASS_C called with name = main. Destructor for CLASS_C called with name = outside.
Unfortunately, if the exit is uncommented then the following output results.
Constructor for CLASS_C called with name = outside. Constructor for CLASS_C called with name = main. Constructor for CLASS_C called with name = my_function. Constructor for CLASS_C called with name = new. Destructor for CLASS_C called with name = new. Destructor for CLASS_C called with name = my_function. Destructor for CLASS_C called with name = outside.
Notice that the destructor for main_C was never called which could mean that the program would not properly clean up something associated with that object. For this reason, I recommend using the new and delete operators whenever possible and to avoid using the exit function.
In C only one function in a given scope with the same name is allowed. However, in C++ more than one function in the same scope can have the same name as long as the functions have different arguments. Such functions are called overloaded. Constructors, stand-alone and class member functions can all be overloaded.
Example: c++_ex4.cc
#include <stdio.h> void output(int i) { printf("output int: %d\n",i); } void output(float f) { printf("output float: %f\n",f); } main() { int integer; float floating_point; integer = 501; floating_point = 1.41; output(integer); output(floating_point); }
This example produces the following output after being compiled and run.
output int: 501 output float: 1.410000
The function output was overloaded so that you can give it either an integer or a floating point number and it will print the value out.
Inheritance in C++ allows programmers to create classes that are extended or modified versions of existing classes without modifying the original class. The derived class will have all of the members of original class plus any members of it's own. If the original class had virtual member functions, they can effectively be replaced in the derived class by declaring new functions with the same name and arguments.
Example: c++_ex5.cc
#include <stdio.h> class BASE_CLASS_A { public: int ia; void print_ia(); }; class DERIVED_CLASS_B: public BASE_CLASS_A { public: float fb; void print_fb(); }; void BASE_CLASS_A::print_ia() { printf("ia = %d\n", ia); } void DERIVED_CLASS_B::print_fb() { printf("fb = %f\n", fb); } main() { DERIVED_CLASS_B b; b.ia = 1; b.fb = 2.0; b.print_ia(); b.print_fb(); }
This example produces the following output after being compiled and run.
ia = 1 fb = 2.000000
The example shows that DERIVED_CLASS_B could be used as if it were defined as it is below.
class DERIVED_CLASS_B { public: int ia; // inherited from BASE_CLASS_A void print_ia(); // inherited from BASE_CLASS_A float fb; // added to derived class void print_fb(); // added to derived class };
Virtual functions allow programmers to write code that is extremely general, because the selection of which functions are called does not occur until run-time. To use virtual functions in this way it is necessary to call them through a pointer to the base class, because only by using a pointer can the type of object passed to a function be changed at run-time. It is necessary to declare the function virtual to obtain run-time binding because of the following fact. If a function which is not virtual is overloaded in a derived class, then the derived class version of the function will be used if the derived class is used explicitly, but not if it is used though a pointer to the base class.
Example: c++_ex6.cc
#include <stdio.h> class BASE_CLASS_C { public: BASE_CLASS_C(char *_name) {name = _name;} ; char *name; virtual void virtual_function(); void non_virtual_function(); }; void BASE_CLASS_C::virtual_function() { printf("BASE_CLASS_C::virtual_function called with name = %s\n",name); } void BASE_CLASS_C::non_virtual_function() { printf("BASE_CLASS_C::non_virtual_function called with name = %s\n",name); } class DERIVED_CLASS_D: public BASE_CLASS_C { public: DERIVED_CLASS_D(char *_name): BASE_CLASS_C(_name) {}; virtual void virtual_function(); void non_virtual_function(); }; void DERIVED_CLASS_D::virtual_function() { printf("DERIVED_CLASS_D::virtual_function called with name = %s\n",name); } void DERIVED_CLASS_D::non_virtual_function() { printf("DERIVED_CLASS_D::non_virtual_function called with name = %s\n",name); } int function_using_ptr_to_base_class(BASE_CLASS_C *ptr_to_c) { ptr_to_c->virtual_function(); ptr_to_c->non_virtual_function(); } main() { BASE_CLASS_C c("c"); DERIVED_CLASS_D d("d"); printf("calling c.virtual_function()\n"); c.virtual_function(); printf("calling c.non_virtual_function()\n"); c.non_virtual_function(); printf("calling d.virtual_function()\n"); d.virtual_function(); printf("calling d.non_virtual_function()\n"); d.non_virtual_function(); printf("calling function_using_ptr_to_base_class(&c;)\n"); function_using_ptr_to_base_class(&c;); printf("calling function_using_ptr_to_base_class(&d;)\n"); function_using_ptr_to_base_class(&d;); }
This example produces the following output after being compiled and run.
calling c.virtual_function() BASE_CLASS_C::virtual_function called with name = c calling c.non_virtual_function() BASE_CLASS_C::non_virtual_function called with name = c calling d.virtual_function() DERIVED_CLASS_D::virtual_function called with name = d calling d.non_virtual_function() DERIVED_CLASS_D::non_virtual_function called with name = d calling function_using_ptr_to_base_class(&c;) BASE_CLASS_C::virtual_function called with name = c BASE_CLASS_C::non_virtual_function called with name = c calling function_using_ptr_to_base_class(&d;) DERIVED_CLASS_D::virtual_function called with name = d BASE_CLASS_C::non_virtual_function called with name = d
Notice the last two lines of the output where the program ends up calling the virtual_function of the derived_class and the non_virtual_function of the base class. Also notice that this example shows how to use an inline constructor and how to pass an argument from the constructor of a derived class to the constructor of the base class.
Last Modified: 08/25/99
If you have questions or comments regarding this page, please contact Will Shackleford at
shackle [at] cme.nist.gov (shackle[at]cme[dot]nist[dot]gov)