BogoToBogo
  • Home
  • About
  • Big Data
  • Machine Learning
  • AngularJS
  • Python
  • C++
  • go
  • DevOps
  • Kubernetes
  • Algorithms
  • More...
    • Qt 5
    • Linux
    • FFmpeg
    • Matlab
    • Django 1.8
    • Ruby On Rails
    • HTML5 & CSS

Class: User-Defined Data Types

cplusplus_icon.png




Bookmark and Share





bogotobogo.com site search:




Classes vs Structs

Classes and structs are essentially the same except structs' default access modifier is public.


The struct is a carry-over from the C. In C++, classes are generally used.

Class Struct
class Point { public: double x,y; } Struct Point { double x,y; }
private by default public by default




Class Example

A simple class of Vectors with Point class
#include <iostream>

using namespace std;

class Point                       
{
public:
	double x, y;
};

class Vector
{
public:
	Point start, end;
};

int main()
{
	Vector vec1;
	vec1.start.x = 1.0;
	vec1.start.y = 2.0;
	vec1.end.x = 3.0;
	vec1.end.y = 4.0;

	Vector vec2;      
	vec1.end.x = 5.0;
	vec1.end.y = 6.0;
	vec2.end.x = 7.0;
	vec2.end.y = 8.0;
}


Passing a class to a function

Let's add a function to help the Point class so that it can use offset() function and build the second point:

#include <iostream>

using namespace std;

class Point                       
{
public:
	double x, y;
};

class Vector
{
public:
	Point start, end;
};

// pass by value
void offset(Point p , double dx, double dy)
{
	p.x += dx;
	p.y += dy;
}

int main()
{
	Point p;
	p.x = 1.0;
	p.y = 2.0;
	offset(p, 3.0, 4.0);
	cout << "p(" << p.x << "," << p.y << ")" << endl; // unchanged (1,2)
	return 0;
}

As we see from the result, passing by value passes a copy of the class instance to the function, and the changes made in the function aren't preserved.

We need to pass the class using a reference to the class so that the changes are reflected in the original:

#include <iostream>

using namespace std;

class Point                       
{
public:
	double x, y;
};

class Vector
{
public:
	Point start, end;
};

// pass by reference
void offset(Point &p;, double dx, double dy)
{
	p.x += dx;
	p.y += dy;
}

int main()
{
	Point p;
	p.x = 1.0;
	p.y = 2.0;
	offset(p, 3.0, 4.0);
	cout << "p(" << p.x << "," << p.y << ")" << endl; // changed (4,6)
	return 0;
}


Method - a member function of a class

In the previous example, the offset() function is closely related to the Point class, and there could be many more functions that we can tie them to the class. The following code demonstrates how we link the functions to the class. We call the function a member function or a method:

#include <iostream>

using namespace std;

class Point                       
{
public:
	void offset(double offsetX, double offsetY)
	{
		x += offsetX;
		y += offsetY;
	}
	
	void print()
	{
		cout << "(x,y)=" << "(" << x << "," << y << ")" << endl;
	}

	double x, y;
};

class Vector
{
public:
	void offset(double offsetX, double offsetY)
	{
		start.x += offsetX;
		start.y += offsetY;
		end.x += offsetX;
		end.y += offsetY;
	}

	void print()
	{
		cout << "start=(" << start.x << "," << start.y << ")" << endl;
		cout << "end=(" << end.x << "," << end.y << ")" << endl;
	}

	Point start, end;
};

int main()
{
	Point p1;
	p1.x = 1.0;
	p1.y = 2.0;
	Point p2;
	p2.x = 3.0;
	p2.y = 4.0;
	p1.offset(5.0, 6.0);
	p2.offset(7.0, 8.0);
	p1.print();
	p2.print();

	Vector v1;
	v1.start = p1;
	v1.end = p2;

	Vector v2;
	v2 = v1;
	v2.offset(10.0, 20.0);
	v1.print();
	v2.print();

	return 0;
}

Output from the run:

(x,y)=(6,8)
(x,y)=(10,12)
start=(6,8)
end=(10,12)
start=(16,28)
end=(20,32)

In the example, we made two functions as member functions (methods): offset() and print().





Initialization of a Class - Constructor

Manually initializing our fields of a class can get tedious. So, we need to initialize them when we create an instance. A method called when we are creating an instance is a constructor.

class Point                       
{
public:
	Point()
	{
		x = 0.0;
		y = 0.0;
	}

	double x, y;
};


Constructor can accept parameters
#include <iostream>

using namespace std;

class Point                       
{
public:
	Point(double xi, double yi)
	{
		x = xi;
		y = yi;
	}

	void print()
	{
		cout << "(x,y)=" << "(" << x << "," << y << ")" << endl;
	}

	double x, y;
};

int main()
{
	Point p1(1.0, 2.0);
	Point p2(3.0, 4.0);

	p1.print();
	p2.print();

	return 0;
}

Output is:

(x,y)=(1,2)
(x,y)=(3,4)


Multiple constructors

A class can have multiple constructors:

#include <iostream>

using namespace std;

class Point                       
{
public:
	// default constructor
	Point()
	{
		x = 0.0;
		y = 0.0;
		cout << "default constructor" << endl;
	}

	// constructor with two parameters
	Point(double xi, double yi)
	{
		x = xi;
		y = yi;
		cout << "two-parameter constructor" << endl;
	}

	void print()
	{
		cout << "(x,y)=" << "(" << x << "," << y << ")" << endl;
	}

	double x, y;
};

int main()
{
	Point p(3.0, 4.0);  
        Point q = p;  // q.x = 3.0, q.y = 4.0

	return 0;
}




Copy Constructor

When we assign one class instance to another, it copies all fields from one class and constructs a new instance. Actually, behind the scene, we are invoking a default copy constructor provided by our compiler.

In the previous example, when we do:

int main()
{
	Point p(3.0, 4.0);  
        Point q = p;  // q.x = 3.0, q.y = 4.0

	return 0;
}

we are invoking a copy constructor.

We can have our own copy constructor:

class Point                       
{
public:
        ...
	Point(Point &p;)
	{
		x = p.x;
		y = p.y;
		cout << "customized copy constructor" << endl;
	}
        ...
};


Why do we need a copy constructor?

Let's look at the following example:

#include <iostream>

using namespace std;

class Student 
{
public:
	int id;
	char *name;
	Student() 
	{
		id = 0;
		name = "";
	}
};

int main() 
{
	Student student1;
	student1.id = 10;
	char nm[] = "Clinton";
	student1.name = nm;

	Student student2 = student1;
	student2.name[0] = 'P';
	cout << student1.name; // Plinton
	return 0;
}

As shown in the example, we made another instance of Student class which is student2 from an existing instance student1. However, later we modified an element of the name array. It turned out that modifying a field of one instance can affect the other instance, and that's not we wanted.

So, to control the assigning operation, we need our own copy constructor not the default copy constructor that compiler provides:

#include <iostream>

using namespace std;

class Student 
{
public:
	int id;
	char *name;
	Student() 
	{
		id = 0;
		name = "";
	}

	Student(Student &s;)
	{
		id = s.id;
		name = strdup(s.name);
	}
};

int main() 
{
	Student student1;
	student1.id = 10;
	char nm[] = "Clinton";
	student1.name = nm;

	Student student2 = student1;
	student2.name[0] = 'P';
	cout << student1.name << endl; // Clinton
	cout << student2.name << endl; // Plinton
	return 0;
}

Note that strdup() in our own copy constructor does dynamic memory allocation for the character array including the end character '\0' and returns the address of the heap memory. In other words, the new instance has its own memory to store the name not shared one with which it was instantiated from.



Public or Private - Access Modifier

Access modifier defines where our fields/methods can be accessed from.

  1. public: can be accessed from anywhere
  2. private: can only be accessed within the class
#include <iostream>

using namespace std;

class Point                       
{
public:

	Point(double xi, double yi)
	{
		x = xi;
		y = yi;
	}

	void print()
	{
		cout << "(x,y)=" << "(" << x << "," << y << ")" << endl;
	}

        double x, y;
};

int main()
{
	Point p1(1.0, 2.0);

        // allowed
	p1.x = 111;
	p1.y = 222;

	return 0;
}

The member x and y are accessible from outside of a class since their access modifier is public. However, in the example below, it's private, and cannot be accessed from outside.

#include <iostream>

using namespace std;

class Point                       
{
public:
	Point(double xi, double yi)
	{
		x = xi;
		y = yi;
	}

	void print()
	{
		cout << "(x,y)=" << "(" << x << "," << y << ")" << endl;
	}

private:
	double x, y;
};

int main()
{
	Point p1(1.0, 2.0);

        // access not allowed
	p1.x = 111;
	p1.y = 222;

	return 0;
}


get() & set() methods - accessing private members

There is a way to access the private members using member functions of the class:

#include <iostream>

using namespace std;

class Point                       
{
public:
	Point(double xi, double yi)
	{
		x = xi;
		y = yi;
	}

	void print()
	{
		cout << "(x,y)=" << "(" << x << "," << y << ")" << endl;
	}
        
	double getX() { return x;}
	double getY() { return y;}
	void setX(double xi) { x = xi;}
	void setY(double yi) { y = yi;}

private:
	double x, y;
};

int main()
{
	Point p1(1.0, 2.0);

	p1.setX(111);
	p1.setY(222);
	cout << "(" << p1.getX() << "," << p1.getY() << ")" << endl;

	return 0;
}




Revisiting Constructor

Constructor - a method that is called when an instance is created
#include <iostream>

using namespace std;

class Double                       
{
public:
	Double()
	{
		dval = 0.0;
		cout << "default constructor" << endl;
	}

	double dval;
};

int main()
{
	Double d;
	return 0;
}

Output from the run:

default constructor


Default constructor and an array of objects

When making an array of objects, default constructor is invoked on each object.

#include <iostream>

using namespace std;

class Double                       
{
public:
	Double()
	{
		dval = 0.0;
		cout << "default constructor" << endl;
	}

	double dval;
};

int main()
{
	Double d[3];
	return 0;
}

Output should look like this:

default constructor
default constructor
default constructor


Default constructor of a field variable of a class

When making a class instance, the default constructor of its fields are invoked.

#include <iostream>

using namespace std;

class Double                       
{
public:
	Double()
	{
		dval = 0.0;
		cout << "default constructor" << endl;
	}

	double dval;
};

class DoubleWrapper                       
{
public:
	DoubleWrapper()
	{
		cout << "Wrapper default constructor" << endl;
	}

	// field member
	Double m_Double;
};

int main()
{
	DoubleWrapper dw;
	return 0;
}

Output from the run:

default constructor
Wrapper default constructor

As we see from the output, when we make a DoubleWrapper object dw, the line:

	// field member
	Double m_Double;

calls the default constructor for Double class to make m_Double which is a field member variable of the DoubleWrapper class.



Single parameter constructor

We already know that a constructor can take parameters. Here is a constructor that takes one parameter.

#include <iostream>

using namespace std;

class Double                       
{
public:
	Double(double d)
	{
		dval = d;
		cout << "constructor with parameter " << d << endl;
	}

	double dval;
};

int main()
{
	Double d1(12.3);
	Double d2 = 45.6;
	return 0;
}

Output is:

constructor with parameter 12.3
constructor with parameter 45.6

C++ constructors that have just one parameter automatically perform implicit type conversion. As shown in the example above, we can invoke single-parameter constructor via assignment to the appropriate type.

To block this kind of implicit conversion, we use the keyword explicit:

#include <iostream>

using namespace std;

class Double                       
{
public:
	explicit Double(double d)
	{
		dval = d;
		cout << "constructor with parameter " << d << endl;
	}

	double dval;
};

int main()
{
	Double d1(12.3);
	Double d2 = 45.6; // error
	return 0;
}


Single parameter constructor & default constructor

If a constructor with parameters is defined, the default constructor is no longer available.

#include <iostream>

using namespace std;

class Double                       
{
public:
	explicit Double(double d)
	{
		dval = d;
		cout << "constructor with parameter " << d << endl;
	}

	double dval;
};

int main()
{
	Double d1(12.3);  // OK
	Double d2;        // Error: default constructor is not available
	return 0;
}


Single parameter constructor & an array of objects

Without a default constructor, we cannot declare arrays without initializing them.

#include <iostream>

using namespace std;

class Double                       
{
public:

	explicit Double(double d)
	{
		dval = d;
		cout << "constructor with parameter " << d << endl;
	}

	double dval;
};

int main()
{
	// OK
	Double d1[2] = { Double(12.3), Double(45.6) }; 

	// Error: default constructor is not available
	Double d2[3];        
	return 0;
}


Single parameter constructor & zero-parameter constructor

If a constructor with parameters is defined, the default constructor is no longer available. However, we can create a separate 0-argument constructor:

#include <iostream>

using namespace std;

class Double                       
{
public:
	Double() 
	{
		dval = 0.0;
	}

	explicit Double(double d)
	{
		dval = d;
		cout << "constructor with parameter " << d << endl;
	}

	double dval;
};

int main()
{
	Double d1;       // OK
	Double d2(9.87); // OK    
	return 0;
}

Or we can use default arguments:

#include <iostream>

using namespace std;

class Double                       
{
public:
        // one parameter constructor with default value
	explicit Double(double d = 0.0)
	{
		dval = d;
		cout << "constructor with parameter " << d << endl;
	}

	double dval;
};

int main()
{
	Double d1;       // OK
	Double d2(9.87); // OK    
	return 0;
}


new & delete

For more detailed info, visit Memory Allocation.

Using the new is one of the ways to allocate memory, where the memory will remain allocated until we manually de-allocate it. The new returns a pointer to the newly allocated memory:

int *p = new int;
  1. If using int x; the allocation occurs on a region of memory called the stack
  2. If using new int; the allocation occurs on a region of memory called the heap

The delete operator de-allocates memory that was previously allocated using new. It takes a pointer to the memory location:

int *p = new int;
...
delete p;

Here are couple of things to remember regarding the memory allocation and de-allocation:

  1. Don't use the memory after deletion
  2. Don't delete the memory twice
  3. Use delete if memory was allocated by new
  4. When allocating arrays on the stack int arr[SIZE], size must be a constant
  5. If we use new[] to allocate arrays, they can have variable size
  6. De-allocate arrays with delete[] if we used new[] to allocate arrays.


Using the new & delete with Point class

Let's use dynamic memory allocation to make an instance of our Point class.

#include <iostream>

using namespace std;

class Point                     
{
public:
	Point()
	{
		x = 0.0;
		y = 0.0;
		cout << "default constructor" << endl;
	}

	Point(double xi, double yi)
	{
		x = xi;
		y = yi;
		cout << "two args constructor" << endl;
	}

	void print()
	{
		cout << "(" << x << "," << y << ")" << endl;
	}

	double x,y;
};

int main()
{
	Point *p = new Point; 
	Point *q = new Point(3.0, 4.0);
	p->print();
	q->print();
	delete p;  
	delete q;
	return 0;

Output from the run:

default constructor
two args constructor
(0,0)
(3,4)




Destructor

destructor is called by delete

Destructor is called when the class instance gets de-allocated

The following example shows the case when the destructor is called. In this case, it is triggered by delete.

#include <iostream>

using namespace std;

class Point                     
{
public:
	Point()
	{
		x = 0.0;
		y = 0.0;
		cout << "default constructor" << endl;
	}

	~Point()
	{
		cout << "destructor is called" << endl;
	}

	double x,y;
};

int main()
{
	Point *p = new Point;
	delete p;
	return 0;
}

Output put should look like this:

default constructor
destructor is called


destructor is called when stack allocated variable gets out of scope
int main()
{
	if(true) {
		Point p;
	}

	// p gets out of scope 
	return 0;
}

Output is the same:

default constructor
destructor is called




Copy Constructor & Array of Integers

When we call default copy constructor, the pointer is pointing to the original data. The result can be critical since when the copied object can go away with the original when the copied object gets out of scope. This can trigger a non-desirable destructor behavior: trying to delete an already deleted pointer.

#include <iostream>

using namespace std;

class IntegerArray                     
{
public:
	IntegerArray(int size)
	{
		pArray = new int[size];
                this->size = size;
		cout << "default constructor" << endl;
	}

	~IntegerArray()
	{
		cout << "destructor" << endl;
		delete[] pArray;
	}

	int *pArray;
	int size;
};

int main()
{
	IntegerArray ia(2);
	ia.pArray[0] = 10;
	ia.pArray[1] = 20;
	cout << "ia.pArray[0] = " << ia.pArray[0] << endl;
	cout << "ia.pArray[1] = " << ia.pArray[1] << endl;

	if(true) {
		cout << "copy ia to ib" << endl;
		IntegerArray ib = ia;
		cout << "ib.pArray[0] = " << ib.pArray[0] << endl;
		cout << "ib.pArray[1] = " << ib.pArray[1] << endl;

		cout << "ib is getting out of scope" << endl;
                // 1st destructor
	}

	cout << "ia.pArray[0] = " << ia.pArray[0] << endl;
	cout << "ia.pArray[1] = " << ia.pArray[1] << endl;

	return 0;  // 2nd destructor
}

Output from the run should look like this:

default constructor
ia.pArray[0] = 10
ia.pArray[1] = 20
copy ia to ib
ib.pArray[0] = 10
ib.pArray[1] = 20
ib is getting out of scope
destructor
ia.pArray[0] = -17891602
ia.pArray[1] = -17891602
destructor

As we see from the output, destructor was called twice:

  1. When ib goes out of scope, destructor is called (deallocates array), ia.pArray now a dangling pointer.
  2. when ia goes out of scope (main()), its destructor tries to delete the already-deleted array.



How to fix it? - Make our own copy constructor!

By not depending on the default copy constructor, a newly copied object can have its own memory space and does not interfere with the object from which it is copied.

#include <iostream>

using namespace std;

class IntegerArray                     
{
public:
	IntegerArray(int size)
	{
		pArray = new int[size];
		this->size = size;
		cout << "default constructor" << endl;
	}

	~IntegerArray()
	{
		cout << "destructor" << endl;
		delete[] pArray;
	}

        // copy constructor
	IntegerArray(IntegerArray &arr;)
	{
		pArray = new int[arr.size];
		size = arr.size;
		for (int i = 0; i < size; ++i)
			pArray[i] = arr.pArray[i];
	}

	int *pArray;
	int size;
};

int main()
{
	IntegerArray ia(2);
	ia.pArray[0] = 10;
	ia.pArray[1] = 20;
	cout << "ia.pArray[0] = " << ia.pArray[0] << endl;
	cout << "ia.pArray[1] = " << ia.pArray[1] << endl;

	if(true) {
		cout << "copy ia to ib" << endl;
		IntegerArray ib = ia;
		cout << "ib.pArray[0] = " << ib.pArray[0] << endl;
		cout << "ib.pArray[1] = " << ib.pArray[1] << endl;

		cout << "ib is getting out of scope" << endl;
	}

	cout << "ia.pArray[0] = " << ia.pArray[0] << endl;
	cout << "ia.pArray[1] = " << ia.pArray[1] << endl;

	return 0;
}

The output is different from the previous example:

default constructor
ia.pArray[0] = 10
ia.pArray[1] = 20
copy ia to ib
ib.pArray[0] = 10
ib.pArray[1] = 20
ib is getting out of scope
destructor
ia.pArray[0] = 10
ia.pArray[1] = 20
destructor



Declaration Order

This is from Google C++ Style Guide.

Use the specified order of declarations within a class: public: before private:, methods before data members (variables), etc.

Our class definition should start with its public: section, followed by its protected: section and then its private: section. If any of these sections are empty, omit them.

Within each section, the declarations generally should be in the following order:

  1. Typedefs and Enums
  2. Constants (static const data members)
  3. Constructors
  4. Destructor
  5. Methods, including static methods
  6. Data Members (except static const data members)

Friend declarations should always be in the private section, and the DISALLOW_COPY_AND_ASSIGN macro invocation should be at the end of the private: section. It should be the last thing in the class.
Method definitions in the corresponding .cc file should be the same as the declaration order, as much as possible.
Do not put large method definitions inline in the class definition. Usually, only trivial or performance-critical, and very short, methods may be defined inline.




Call order when we have derived class and instance member initialization list

In the example, we have a class B which inherits from a class A. When we try to construct an instance of B, it needs to construct its base object of A. So, it calls A ctor. Then, in its constructor, the class B has an initialization list which also needs to construct two A instance members, A x and A y. So, it calls A ctor twice with different m member for each instance of A. After that, it finally executes the body of B ctor.

  1. A ctor at B(int n)
  2. Construct A obj x at x(m+1)
  3. Construct A obj y at y(n)
  4. B ctor and execute its body
#include <iostream>
using namespace std;

class A
{
public:
    A(int n = 7) : m(n) { 
	cout << "A() n = " << n 
             << " m = " << m << endl; 
    }
    ~A() { cout << "~A() m = " << m << endl; }
protected:
    int m;
};

class B : public A
{
public:
    // (1) A ctor at B(int n)
    // (2) Construct A obj x at x(m+1)
    // (3) Construct A obj y at y(n)
    // (4) B ctor and execute its body
    B(int n) : x(m + 1) , y(n) { 
		cout << "B() n = " << n << endl;
	}

public:
    // (1) dtor of B
    // (2)-(4) dtor of A
    ~B() {
        cout << "~B() m = " << m << endl;
        --m;
    }
private:
    A x;
    A y;
};

int main()
{
    { B b(10); }

    return 0;
}

Output:

A() n = 7 m = 7
A() n = 8 m = 8
A() n = 10 m = 10
B() n = 10
~B() m = 7
~A() m = 10
~A() m = 8
~A() m = 6

As we see from the output, when we finished the bock of code { B b(10); } in main(), destructors are called. First, B dtor, then A dtor three times since we construct A instances three times.





Forward Declaration

Compiler should know the size of the type!

In C++, classes can be forward-declared if we only need to use the pointer to the class type.
This is because all object pointers are the same size, and this is what the compiler cares about!
This is why a class contains a member that is a pointer to forward declared class (note that the usage of a pointer member is to avoid circular references).

When we simply forward-declare a class, we call it an incomplete type: we do not know the structure of it, and as a result, we do not know the actual size of the class.

There are cases when the forward declaration of a class is not sufficient if we need to use the actual class type, for example, if we have a member whose type is that class directly (not a pointer), or if we need to use it as a base class, or if we need to use the methods of the class in a method.

// Forward declaration of class A
class A;

// class B is OK
class B
{
public:
	B(A* pa, const A& ra) : pA(pa), rA(ra) {};
	A& getRef() const;
	A* getPtr();
	void foo(A b);
	A getA() const;
private:
	A *pA;
	const A &rA;
};

// class C & D have some issues!

// class A cannot be used as a base class
class C: public A {}  ;   // Error!

class D
{
public:
	void set(A* pa) {pA = pa;}

	// used as a parameter for prototype
	void foo(A);  // OK

	// defining a function requires complete type
	void bar(A a) {}  // Error!

	void method() {
		// using a point to an incomplete class type 
		pA->getA();  // Error!
	}
private:
	// cannot be used to declare a member
	A mObj;  //Error!  

	A* pA,   // OK
};

For further discussions:
http://stackoverflow.com/questions/553682/when-to-use-forward-declaration.





Struct

Allocating struct
#include <stdlib.h>

struct A
{
	int one;
	int two;
};

int main()
{
        // dynamically allocated struct
	struct A *pA = (struct A *)malloc(sizeof(struct A));
	(*pA).one = 1;
	pA -> two = 2;

        // statically allocated struct
	struct A a;
	a.one = 1;
	a.two = 2;
}


Using typedef
#include <stdlib.h>

struct A
{
	int one;
	int two;
};

typedef struct A A_t;

int main()
{
	A_t *pA = (A_t *)malloc(sizeof(A_t));
	(*pA).one = 1;
	pA -> two = 2;

	A_t a;
	a.one = 1;
	a.two = 2;
}

Visit structs vs classes.



Making a linked list

There are some examples, visit Linked List Examples.

The following code shows a very simple linked list.

#include <stdlib.h>
#include <stdio.h>


typedef struct node
{
	struct node *next;
	int data;
}	node_t;

node_t *make_node(int d)
{
	node_t *new_node = (node_t *)malloc(sizeof(node_t));
	new_node -> data = d;
	new_node -> next = NULL;
	return new_node;
}

// insert a node before the given pointer
node_t *insert_node(node_t *ptr, int d)
{
	node_t *new_node = make_node(d);
	new_node -> next = ptr;
	return new_node; 
}

// delete a given node
node_t *delete_node(node_t *head, int d)
{
	if(head == NULL) return NULL;

	if(head->data == d) {
		node_t *new_head = head->next;
		free(head);
		return new_head;
	}
	node_t *prev = head;
	node_t *cur = head->next;
	while(cur) {
		if(cur->data == d) {
			prev->next = cur->next;
			free(cur);
			return NULL;
		}
		cur = cur->next;
	}
}

int main()
{
	node_t *head = make_node(1);
	head = insert_node(head, 2);
	head = insert_node(head, 3);
	head = insert_node(head, 4);
	head = insert_node(head, 5);

	node_t *ptr = head;
	while(ptr) {
		printf("ptr->data=%d\n", ptr->data);
		ptr = ptr->next;
	}

	head = delete_node(head, 5);
	delete_node(head, 3);

	ptr = head;
	while(ptr) {
		printf("ptr->data=%d\n", ptr->data);
		ptr = ptr->next;
	}

	return 0;
}

The output from the run should look like this:

ptr->data=5
ptr->data=4
ptr->data=3
ptr->data=2
ptr->data=1
ptr->data=4
ptr->data=2
ptr->data=1



Class Hierarchy Diagram

We can construct a diagram for class hierarchy using Doxygen and Graphviz.

  1. Go to the source directory
  2. Run "doxygen -g"
    This will generate a configure file "Doxyfile"
  3. Modify the "Doxyfile" to get the diagrams as well as documents.
    change the following options of the generated Doxyfile:
    EXTRACT_ALL            = YES
    HAVE_DOT               = YES
    UML_LOOK               = YES
    
  4. run "doxygen Doxyfile"
  5. Navigate to the "html" directory, and run "index.html"

Sample_Hierarchy.png

Sample_Diagram.png






night




Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization

YouTubeMy YouTube channel

Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






C++ Tutorials

C++ Home

Algorithms & Data Structures in C++ ...

Application (UI) - using Windows Forms (Visual Studio 2013/2012)

auto_ptr

Binary Tree Example Code

Blackjack with Qt

Boost - shared_ptr, weak_ptr, mpl, lambda, etc.

Boost.Asio (Socket Programming - Asynchronous TCP/IP)...

Classes and Structs

Constructor

C++11(C++0x): rvalue references, move constructor, and lambda, etc.

C++ API Testing

C++ Keywords - const, volatile, etc.

Debugging Crash & Memory Leak

Design Patterns in C++ ...

Dynamic Cast Operator

Eclipse CDT / JNI (Java Native Interface) / MinGW

Embedded Systems Programming I - Introduction

Embedded Systems Programming II - gcc ARM Toolchain and Simple Code on Ubuntu and Fedora

Embedded Systems Programming III - Eclipse CDT Plugin for gcc ARM Toolchain

Exceptions

Friend Functions and Friend Classes

fstream: input & output

Function Overloading

Functors (Function Objects) I - Introduction

Functors (Function Objects) II - Converting function to functor

Functors (Function Objects) - General



Git and GitHub Express...

GTest (Google Unit Test) with Visual Studio 2012

Inheritance & Virtual Inheritance (multiple inheritance)

Libraries - Static, Shared (Dynamic)

Linked List Basics

Linked List Examples

make & CMake

make (gnu)

Memory Allocation

Multi-Threaded Programming - Terminology - Semaphore, Mutex, Priority Inversion etc.

Multi-Threaded Programming II - Native Thread for Win32 (A)

Multi-Threaded Programming II - Native Thread for Win32 (B)

Multi-Threaded Programming II - Native Thread for Win32 (C)

Multi-Threaded Programming II - C++ Thread for Win32

Multi-Threaded Programming III - C/C++ Class Thread for Pthreads

MultiThreading/Parallel Programming - IPC

Multi-Threaded Programming with C++11 Part A (start, join(), detach(), and ownership)

Multi-Threaded Programming with C++11 Part B (Sharing Data - mutex, and race conditions, and deadlock)

Multithread Debugging

Object Returning

Object Slicing and Virtual Table

OpenCV with C++

Operator Overloading I

Operator Overloading II - self assignment

Pass by Value vs. Pass by Reference

Pointers

Pointers II - void pointers & arrays

Pointers III - pointer to function & multi-dimensional arrays

Preprocessor - Macro

Private Inheritance

Python & C++ with SIP

(Pseudo)-random numbers in C++

References for Built-in Types

Socket - Server & Client

Socket - Server & Client 2

Socket - Server & Client 3

Socket - Server & Client with Qt (Asynchronous / Multithreading / ThreadPool etc.)

Stack Unwinding

Standard Template Library (STL) I - Vector & List

Standard Template Library (STL) II - Maps

Standard Template Library (STL) II - unordered_map

Standard Template Library (STL) II - Sets

Standard Template Library (STL) III - Iterators

Standard Template Library (STL) IV - Algorithms

Standard Template Library (STL) V - Function Objects

Static Variables and Static Class Members

String

String II - sstream etc.

Taste of Assembly

Templates

Template Specialization

Template Specialization - Traits

Template Implementation & Compiler (.h or .cpp?)

The this Pointer

Type Cast Operators

Upcasting and Downcasting

Virtual Destructor & boost::shared_ptr

Virtual Functions



Programming Questions and Solutions ↓

Strings and Arrays

Linked List

Recursion

Bit Manipulation

Small Programs (string, memory functions etc.)

Math & Probability

Multithreading

140 Questions by Google



Qt 5 EXPRESS...

Win32 DLL ...

Articles On C++

What's new in C++11...

C++11 Threads EXPRESS...

Go Tutorial

OpenCV...








Contact

BogoToBogo
contactus@bogotobogo.com

Follow Bogotobogo

About Us

contactus@bogotobogo.com

YouTubeMy YouTube channel
Pacific Ave, San Francisco, CA 94115

Pacific Ave, San Francisco, CA 94115

Copyright © 2024, bogotobogo
Design: Web Master