Skip to content

Instantly share code, notes, and snippets.

@wutipong
Created May 1, 2018 12:20
Show Gist options
  • Save wutipong/2b4308d6da438bdfc8e5822550dd3897 to your computer and use it in GitHub Desktop.
Save wutipong/2b4308d6da438bdfc8e5822550dd3897 to your computer and use it in GitHub Desktop.

C++ for Java Programmer.

notice

Some construct that is almost identical between Java and C++ (eg. loop statement, condition statement) may not be discussed in this document.

Hello World

#include <iostream>

int main(int argc, char** args) {
  std::cout<<"Hello World\n"

  return 0;
}

The entry point of standard C++ program is main(). This is however depends on the platform of the application as well. In Windows' WIN32 has its entry point as WinMain() function.

To sent texts into the console, standard library provides console output stream object called std::cout. Use the insertion operator << to send strings and variables into the stream. To end the line, put the \n to break the line, or send the std::endl into the stream.

Preprocessor

Preprocessors are special instruction provided to compiler to do some specific command to the code it's being compile. In contrast to Java's attributes, preprocessors does not care about the code unit it's working with. Preprocessors works with the source code level instead of the language level.

The compiler will process the source file, until there is no preprocessors left in the the file, before start compiling the code.

#define

#define is used to define macros. Macro is the text processing command, which replace the macro call with the predefined text.

For example, the code below :-

#define PI 3.1416
#define MIN(X, Y)  \
    X < Y? X : Y

double radius = 30.0;
double area = PI * radius * radius;

long min = MIN(5, 6);

Will be roughly translated into:

double radius = 30.0;
double area = 3.1416 * radius * radius;

long min = 5 < 6? 5: 6;

#include

#include is used to bring the content of a file into the caller. For example.

a.h

int a;

a.cpp

#include "a.h"

a += 1;

Will results in

int a;

a += 1;

Usually #include is used with header files, which contains only declaration. The #include preprocessor has two form of parameter

  • #include <iostream> The <> form is for system library and includes file from external library.
  • #include "abc.h" The "" form is for the header file withing the same project.

Since the code is processed repeatedly until no preprocessor is left, it's possible that the same file is included multiple times. Consider:

a.h

int a;

b.h

#include "a.h"

a = 2;

a.cpp

#include "a.h"
#include "b.h"

a += 1;

When compiling a.cpp, the code will be preprocessed into something like this: a.h

int a;

b.h

#include "a.h"

a = 2;

a.cpp

int a;
int a;

a = 2;

a += 1;

Which causes the variable a redefinition.

To solve this issue, the technique call inclusion guard is used by using macro #ifndef and #define inside the header file. For example:-

a.h

#ifndef A_H
#define A_H

int a;
#endif // #ifndef A_H

After the file a.cpp is processed for the first time, it will be something like.

#ifndef A_H
#define A_H

int a;
#endif // #ifndef A_H

#ifndef A_H
#define A_H

int a;
#endif // #ifndef A_H

a = 2;

a += 1;

Then it's processed one more time. At the first #ifndef the condition is true (macro A_H is not defined at this point.). Then the inner preprocessor run, A_H is defined by the next line.

After the that, at the second #endif, the condition is false (A_H is previously defined), so the whole blocked is removed.

Finally the results code will be something like :-

int a;

a = 2;

a += 1;

Basic Language Concept

name and namespace.

Identifiers (variables, function, class, etc.) in C++ are given name. When there's a lot of names, it is possible that the name may collide to each other.

To prevent name collision, Identifiers can be assign to a namespace. This is very similar to Java's package, but since C++ supports global identifiers, a namespace may contain variables and functions (as well as classes like package).

Below is how a namespace is define.

namespace test {
  int var;
  int function(){
    return 0;
  }
}

To access a name insides a namespace, use a scope resolution operator ::. For example.

test::var = 10;
test::function();

A namespace can contain namespaces as well

namespace company {
  namespace dev {
    void fire(int employeeid){}
  }

  namespace qa {
    void fire(int employeeid){}
  }
}

By putting variables or functions inside a namespace does not mean it is contained inside a scope. A identifiers remains global, only its name is kept inside a namespace.

Sometime it's needed to refer to an identifier inside a namespace frequently. using keyword can be used to make the identifier name available to the current program.

namespace test {
  int var;
  int function(){
    return 0;
  }
}

using test::var;
using test::function;

var = 10;
function();

Also if we want every identifier inside a namespace available, we can use using namespace to exposed all identifier.

namespace test {
  int var;
  int function(){
    return 0;
  }
}

using test::var;
using test::function;

var = 10;
function();

Statement

Statements in C++ are very similar to Java. They are ended by ;.

int a;
a = 100;
return;

Compound statements or blocks are grouped inside curly brackets {}.

{
  int a;
  a = 100;
}

Control Statement

if and else

if checks the condition and proceed with the following statement if the condition is true. else, if presents, perform its following statement when the if condition fails.

if(input % 2 == 0) std::cout << "Even Number" << std::endl;
else std::cout << "Odd Number" << std::endl;

switch/case

switch compare the input against all case value. If the input is found, it start performing at the statements the case label is placed at, until break is found.

The case label must contains constant numeric value. Thus it cannot be used with anything else rather than integer (enumerator can be used as well.). default is a special label which tagged at the statement it should start at when the input does not match any other label.

For example:

switch(input) {
  case 0:
    std::cout << "ZERO" << std::endl;
    break;
  case 1:
    std::cout << "ONE" << std::endl;
    break;
  case 7:
    std::cout << "SEVEN" << std::endl;
  default:
    std::cout << "HEAVEN" << std::endl;
    break;
}

From above example, if the input is 7, the output would be

SEVEN
HEAVEN

While other cases would print only one line. This phenomenal is called "fall-through".

while and do/while

while statements checks the condition and proceed with the following statement if the condition is true. After the statement is executed, the condition is rechecked again. This is repeated until the condition is false.

while(input < 0) input--;

do/while performs the statement following do keyword first, and then the condition is checked. If the condition is true then it re-perform the statement again. This is done repeatedly until the condition is false.

do input--;
while (input >= 0);

for

for works just like what in Java.

for(int i = 0; i< SIZE; i++)
  std::cout<<i<<std::endl;

range for

This is an other form of for loop. Essentially it's iterate through a container while providing a read-only iterator for each object in a loop.

int array[] = {1, 2, 3 ,4};

for(auto element: array) {
  std::cout<<element;
}

Objects and Variables.

In Java, objects or class instances are constructed by using new operator. For example:

Object a;
a = new Object();

In the other hands, creating a variable with basic type does not require new operator.

int a;
a = 10;

Variable with a class type is a reference to an object which is stored in an free-store memory area called heap. A reference is a variable which keeps the memory address of an object, it does not keep the object value by itself. In the other hand, variable with basic type is stored in the stack directly.

Assigning a variable values into another variable will cause the value to be copied over. References copy the memory address it is holding (not the object it's referring to), while basic type variable copy the value of it self to another.

For example:

ArrayList<String> a = new ArrayList<String>(); // a holds an memory address. 0x00112233
ArrayList<String> b = a; // b holds the memory address 0x00112233 as well.

b.add("Hello World"); // The object pointed by a and b has a new object.

int a = 10;
int b = a; //b = 10

b += 5; //a is still 10

In C++, objects or class instances does not need to be kept in the heap, nor needs a reference to keep the value. Class object in C++ can lives in stack just like the numeric data types.

For example.

std::string a("Hello World");

Class instances can be created insides heap memory as well, using the same new operator. The difference here is the variable will be a pointer.

std::string *ptr = new std::string("Hello World");

In the other hand, basic data type object can be created inside the heap memory as well.

int *ptr = new int(5);

This is not permitted by Java, which the boxed class (e.g. java.lang.Integer) has to be used instead.

Constant variable

A variable can be declared as constant by putting a const keyword in front of the declaration. The value must be assigned right away otherwise it results in compile failure.

const int a = 10;

Objects lifetime

In Java, when creating a variable, the variable lives in a stack. When the enclosing block ends, the stack pops all variable declared in that blocks out, and caused them to be destroyed and inaccessible.

if(isTrue()) {
  int a = 10;
  System.out.println(a);
}// a is destroyed here.

This is the same for both basic types and references.

However, as mentioned before that the class instances are created in the heap, the object is not destroyed immediately after the block ends.

if(isTrue()) {
  String a = new String("Hello... Is it me you're looking for ?");
  System.out.println(a);
}// a is destroyed here, but the String instance it refers to is not.

Java provides a mechanism called Garbage Collector or GC which is responsible for destroying referred objects. GC is invoked occasionally during runtime. Thus manually object destruction is not required.

C++ does not have a GC per se, so the developer is responsible to destroy the heap object manually. This is only required for objects that lives in the heap. To destroy the object, use delete operator.

if(isTrue()) {
  std::string* a = new std::string("I can see it in your eyes."); //notice the `new` operator
  delete a; // a is actually still accessible, but the instance it points to is no longer valid.
} // a is destroyed here, but the string instance was destroyed before it reached.

Class instances that are created inside the stack, e.g. the one created without 'new' operator, will be destroyed automatically in the same fashion as the basic data types (when the enclosing block ended).

if(isTrue()) {
  std::string a("I can see it in your smile.");
  std::cout << a;
}// a is destroyed here. Full stop.

Array

Array is a group of value of the same type, sitting next to each other. C++ array is a little bit restricted than what in Java. This will be discussed later on in this topic.

To declare an array:

int arrayOfInt[10];

The size of array has to be known, as it's a part of the type. It has to be a constant integer value. Below code does not compile.

int size1 = 10;
int array1[size]; // array size is not constant.

const int size2 = 10;
int array2[size2]; //array size is not constant, although it is const integer.

An array can be assigned its value at the declaration, using initializer list, a CSV list inside a curly braced. The size value can be omitted in this case.

int array[] = {0,1,2,3};

Accessing element inside an array can be done by similar construct as Java.

for(int i = 0; i < 10 ; i++)
    std::cout<<array[i]<<std::endl;

There's no direct way to get the size of an array. Something like .length in Java is not available in C++. Also C++ does not prevent the code from accessing an element outside the array (unexpected behavior). The code that use array must know the size of array before using the array.

If the array size should be configurable, use a macro for its size.

#define ARRAY_SIZE 10
int array[ARRAY_SIZE];

for(int i = 0; i < ARRAY_SIZE ; i++)
    std::cout<<array[i]<<std::endl;

Pointers

Referrence in Java has the same characteristic as C++'s pointer. It's an object that contains memory address of another object. Unlike Java's reference, however, C++'s pointer is not limited to keep the address of heap object. It can keep the memory address of an object in the stack as well.

int a = 10;
int *p = &a; // p has the value of a's memory address.
             //It's commonly said like p is pointing to a.

& operator, when placing in front of a variable, returns the memory address of the variable.

Pointer can be assigned to point to nothing by using either NULL or nullptr.

int *p1 = NULL;
int *p2 = nullptr;

NULL exists long before nullptr, it's used commonly in C for a very long time. nullptr is the preferred way to refer to nothing. It's defined by C++11.

To access the value of object pointed by a pointer, use the indirect operator * in front of the pointer. This operator has very low precedence so it's a good idea to wrap around itself and the variable with parenthesis.

int a = 10;
int *p = &a; // p points to a

std::cout<< *p <<endl; // print 10 into the console.
(*p) += 10; // a is now 20.

Since pointers are just basically numeric type holding memory address, arithmetic operations can be applies to pointers. However these operator does not work the same way as the numeric type. Adding 1 into a pointer will make it points to the next object in the memory, not adding the value it's holding by 1. Subtracting works the same way.

int a = 0; // supposedly a is kept in the address 0x00112233
int *p = &a;
p++; // p is now pointing to an integer next to a.
     // to be precise, its value is now 0x00112237 (=0x00112233 + sizeof(int))

Since array is basically an adjacent group of value, pointers can be used like a iterator over an array as well. Consider:

int array[10];
int *it = array;

it += 2 // it points to array[2]

As a plus, a subscription operator return the value of the Nth object after the object pointed by the pointer.

int array[] = {0,1,2,3,4,5,6,7};
int *it = array;

std::cout<<it[2]; //<<print "2"

This make a pointer looks like an array. And, in fact, pointer can be used as an array, which will be discussed in the next topic.

Dynamic Array.

Dynamic array is a way to allocate a block of memory in the heap, and returns the reference to that memory block. The memory block is allocated with the size of an object multiplied with the input number. This memory allocation is done during runtime, so the memory size is not needed to be known by the compiler at the runtime. A variable can be used here.

To allocate a memory, use new [] operator.

int size = 200;
int *dynamicAray = new int[size];

One downside of a dynamic array is, since this memory is allocated in the heap memory, it must be manually deallocated using delete[] operator. Otherwise this memory block will leak.

int size = 200;
int *dynamicArray = new int[size];

delete[] dynamicArray;

Reference

Reference in C++, means something different than the same keyword in Java. References is an alias for a variable. For example :

int a = 10;
int &r = a; // r is a reference for a

r += 10; // a is now 20.

Once a reference is assigned to a variable, it cannot be changed to refer to another.

Referring to object's member

In Java, methods and fields can be referred by . operator. This is the same as one provided by C++.

std::string str = "abc";
std::cout<<str.size()<<std::endl;

However, for pointers that point to an object, . cannot be used as the pointer does not hold the object. Instead the indirect member access operator -> has to be used.

std::string *pStr = new std::string("def");
std::cout<<pStr->size()<<std::endl;

Assignments

Assignments in general has two purpose:

  1. Assign a new adhoc value into a variable. int a = 10;
  2. Copy a value from one variable into another. int a = b;

In the 2. specifically, the value of the variable is copied to the assignee variable if they are the same type. If they are not then the value has to be converted to target type using type casting.

In Java, all object variable are reference. When performing an assignment operation, it simply copy the value the reference is holding. In other words, the memory address of the actual copy is assigned to the another variable.

In C++, the reference variable and pointers works pretty much the same way. However, the object variable does not. C++ provides default behavior for copying objects, but this can be overridden or even disallow copying/assignment operation altogether. This will be discussed later in Class section.

Enumerators

Enumerator in C++ is not as flexible as one in Java. It's a data type which has predefined set of possible value. To define an enumerator use enum or enum class keyword.

enum DAY {
  DAY_MONDAY, DAY_TUESDAY, DAY_WEDNESDAY,
  DAY_THURSDAY, DAY_FRIDAY, DAY_SATURDAY
};

enum class Month {
  January, February, March,
  April, May, June,
  July, August, September,
  October, November, December
};

enum class was added in C++11. It is not available in any version before C++11 standard.

The value defined in enumerator are constant integer, it works almost the same way as a constant with an exception as it cannot be referred by reference operator (as it's not an variable). Value defined in enum are exposed globally, while the enum class contains the value within its name. For example

DAY day = DAY_MONDAY;
Month month = Month::April;

The value name defined in enum may clashes with other code, so it's advised to begin the name with the type name. Since enum class contains the value under its name already, this is already enforced.

Each value in an enumerator is an constant integer, the first value is 0, and the next one is larger then the other by 1. Each value can be assigned with a different value manually using assignment operator =. The value that is not specified explicitly will be larger then the one before by 1.

enum class Numeric {
  ONE = 1,
  TWO,
  THREE,
  FOUR,
  ZERO = 0
};

Type Casting.

Type casting allows the value in one type to be converted into another. In Java, the syntax is, for example :-

Object value = this.getValue();
Integer intValue = (Object) value;

In this case it converts the value from Object reference to Integer reference.

This style of casting is taken over from C. C++ also support the same style of type casting

char c = 'a';
int code = (int) c;

This is commonly called C-Style Casting.

C++ provides its own way of type casting. There are 4 types of casting in C++.

1. const_cast

add or remove constant-ness to a value.

const int a = 10;
int b = const_cast<int>(a);
const int c = const_cast<const int>(c)

2. dynamic_cast

Down-casting an object from a pointer to upper class object to a pointer to lower class reference.

Parent *p = Parent::Create();
Child *c = dynamic_cast<Child*>(p);

3. reinterpret_cast

reinterpret_cast allows conversion between integer types and any kind of pointers, or between pointers of different types.

auto p = reinterpret_cast<float*>(5); // p is a pointer points to a float value at the address 0x5

4. static_cast

static_cast converts the value between two types. Basically the compiler checks if the type is convertible between the value and the target type. If it does not then the compile fails.

enum class HOLIDAY {SATURDAY, SUNDAY};

int day = static_cast<int>(HOLIDAY::SATURDAY);

Also if the proper conversion is known to the compiler, it will convert the value into another which means the samething in the target type.

Exception Handling

Exception is a way to handling errors. In c++, the exception value can be any type. For example:

if(value == nullptr) throw 0;

To catch the exception, use try/catch keyword.

#include <iostream>

int test(char a[]) {
  throw 0;
}

int main(int argc, char** argv) {
  try{
    test("abcdefg");
  } catch (int errorCode) {
     std::cout<<"Error Code : " << errorCode << std::endl;
  }

  return 0;
}

catch clause can be chained when the expected code might throw multiple type of value.

  try{
    test("abcdefg");
  } catch (int errorCode) {
    std::cout<<"Error Code : " << errorCode << std::endl;
  } catch (std::string errorText) {
    std::cout<<"Error : " << errorText << std::endl;
  }

use catch(...) to handle the case the type of exception might be unknown.

  try {
    test("abcdefg");
  } catch (int errorCode) {
    std::cout<<"Error Code : " << errorCode << std::endl;
  } catch (std::string errorText) {
    std::cout<<"Error : " << errorText << std::endl;
  } catch (...) {
    std::cout<<"Unknown error"<< std::endl;
  }

Functions

Function is not available in Java. The close equivalent feature of Function is Static Method. Basically it's like a static method but it is not belong to any class.

Here is how to declare a function.

int count(std::string str) {
  return str.length():
}

Forward Declaration

Functions have to be declared first before it can be called. In C++, code that exists before a function cannot call the function. The following code will not compile:

int main(){
  print("Hello");
}

void print(std::string str){
  std::cout<<"str"<<std::endl;
}

Sometime this kind of code is unavoidable. Consider :

int aFunc(int input) {
  if(input > 0)  {
    std::cout << input;
    bFunc(input - 1);
  }
  return 0;
}

int bFunc(int_input) {
  return aFunc(input - 1);
}

The above code will not compile, and shuffling the order does not make it compile as well. In C++, function can be forward declared, by duplicating the function signature and put it above the caller.

int bFunc(int_input);

int aFunc(int input) {
  if(input > 0)  {
    std::cout << input;
    bFunc(input - 1);
  }
  return 0;
}

int bFunc(int_input) {
  return aFunc(input - 1);
}

If this function should be called in many file, the forward declaration can be put in a separated file called header file (_.h, _.hpp), and let the caller include the header file so the function is available for those source file.

print.h

#ifndef PRINT_H
#define PRINT_H
#include <iostream>

void print(std::string str);
#endif

print.cpp

void print(std::string str){
  std::cout<<"str"<<std::endl;
}

main.cpp

#include "print.h"
int main() {
  print("Hello");
}

Reference Function Parameters

In the nature of C++, passing parameter is equivalent to assigning a value to a variable, which basically copying values from one variable to another. Copy large object, or object created from very complex class, can be very expensive. To prevent copying, references can be used as function parameters.

void print(std::string& str){
  std::cout<<str<<std::endl;
}

int main(){
  std::string hello("Hello")
  print(hello);
}

One characteristic of receiving value by reference is, if the parameter is updated inside the function, the value of the caller will be updated too. Also it requires a variable for each parameter.

void printDownToZero(int& number){
  while(number >=0) {
    std::cout<<currentValue;
    currentValue --;
  }
}

int main(){
  int value = 20;
  printDownToZero(value); // <- value is 0 after this line.
}

To prevent this side-effect, we can make the parameters constant reference. However this time the parameters cannot be updated in the function, as it's now constant. If somehow the value has to be updated, without affecting the value of the caller, just assign the value to another variable.

void printDownToZero(const int& number){
  int currentValue = number;
  while(currentValue >=0) {
    std::cout<<currentValue;
    currentValue --;
  }
}

int main(){
  int value = 20;
  printDownToZero(value); // <- value is 20 after this line.
}

Templates

C++ provides the meta-programming functionality in terms of templates. This is somewhat similar to Java's generic where a generic class would looks like:

public class Box<T> {

    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

Meta-programming is a programing style which predefine the functionality around meta-type, an incomplete type which will be replaced by a concrete type when the function/class is used. So from the example above, to instantiate a Box instance, a type parameter has to be supplied to replace the type T defined inside.

For example:

Box<String> stringBox = new Box<String>();

The same example in C++ would be written like :-

template <class T>
class Box {  
private:
  T t;
public:
  void set(T t) { this.t = t; }
  T get() { return t; }
};

An instance will be instantiate like :

Box<std::string> stringBox;
Box<std::string> * pStringBox = new pStringBox<std::string>(); // pointer to heap object version.

Templates can be used in function as well.

template <class T>
T max(T t1, T t2){ return t1 > t2? t1 : t2; }

int v = max<int>(1, 2);

Also if the name can be inferred, it can be omitted entirely:

template <class T>
T max(T t1, T t2){ return t1 > t2? t1 : t2; }

int v = max(1, 2);

Templates are not limited to the type parameter, template parameter can be non-type parameter as well. For example

template <class T, int N>
class vector {
private:
  T element[N];
};

Shortcoming of template is, in order to use template classes or functions, the caller must have access to the whole declaration of the template. Forward declaration across source files causes compilation error.

The below code files might compile, but they will fail at link stage.

max.h

#ifndef MAX_H
#define MAX_H
template <class T> T max(T t1, T t2);
};
#endif//MAX_H

max.cpp

template <class T>
T max(T t1, T t2){ return t1 > t2? t1 : t2; }

main.cpp

#include "max.h"
int main() {
  int m = max(10,5);
}

Class

Class are data structure definition in OOP sense. It defines a type of a object, along with the properties of that object. The properties can be either variables or functions.

In Java, a class can be declared using the construct similar to this :

class User {
   private String username;
   private String password;

   public User(String username, String password) {
     this.username = username;
     this.password = password;
   }

   public String GetUsername() {
     return username;
   }

   public String GetPassword() {
     return password;
   }
}

The similar class can be declared in C++ like the code below.

class User {
public:
  User(const std::string& username, const std::string& password) {
    this->username = username;
    this->password = password;
  }

  std::string GetUsername() {
    return username;
  }

  std::string tPassword() {
    return password;
  }

private:
   std::string username;
   std::string password;

};

By using forward declaration, the class member function can be defined separately from the class definition.

class User {
public:
  User(const std::string& username, const std::string& password) ;

  std::string GetUsername();
  std::string GetPassword();

private:
   std::string username;
   std::string password;

};

User::User(const std::string& username, const std::string& password) {
  this->username = username;
  this->password = password;
}

std::string User::GetUsername() {
  return username;
}

std::string User::GetPassword() {
  return password;
}

Like function forward declaration, the class declaration can be done separately in a header file.

#ifndef USER_H
#define USER_H
class User {
public:
  User(const std::string& username, const std::string& password) ;

  std::string GetUsername();
  std::string tPassword();

private:
   std::string username;
   std::string password;

};
#endif

Class inheritance.

In Java, a class must inherit an other class (by default it inherits java.lang.Object classes.). Class in C++ does not have this constraint. A derived class has all properties defined by the parent, but some properties is not accessible for the parent class.

In C++, to inherit a class, add : BaseClass at the end of the class name.

class  AdminUser : public User {

}

Inheritance can be done in either public private and protected. For the most of the time we'd use the public one as it retains the access level of the properties defined in the parent class.

Also a class in C++ can inheritance multiple classes (multiple Inheritance). For example :

class Rect {
  int x, y, w, h;
};

class Drawable {
  void Draw();
}

class RectDrawable: public Rect, public Drawable {

}

Constructors

Constructors define the behavior when the class object is created. Constructors in C++ is very similar to Java. It looks like a function with no return type and the name is the same name as the class name.

User::User(const std::string& username, const std::string& password) {
  this->username = username;
  this->password = password;
}

A class members can be initialized in the member initialize list, something like :-

User::User(const std::string& username, const std::string& password) :
  username(username), password(password) {}

To refer a constructor of super class, simply refer to the constructor by the super class name.

class SuperUser: public User {
private:
  int level;
public:  
  SuperUser(const std::string& username, const std::string& password, const int& level) :
    User(username, password), level(level) { }
}

By default, all of the default constructors of the super class will be called implicitly if there's not such call in the member initialize list. If there's no default constructors available in any of the parent class, the compile would fails. Default constructors will be discussed next.

Default Constructor.

Default constructors are constructor without parameter. For example.

class A {
public:
  A(): value(10){}
private:
  int value;
};

A a; // << call A::A()

Copy Constructor

Copy constructor is a constructor which takes a parameter of the same class.

class A {
public:
  A(const A& a): value(a.value){}
private:
  int value;
};

A a1;
A a2(a1); // call copy constructor.

Since assignment is actually copying, assignment operator would call copy constructor as well.

A a1;
A a2 = a1; // call copy constructor.

Destructor

Destructor is not available in Java. Basically it is used to free resource that is taken by the object, when the object is destroyed.

A class can only have one destructor.

template <class T>
class FixedStacked {
private:
  T* buffer;
  int size;
  T* iter;

public:
  FixedStacked(const int& size) : size(size) {
    buffer = new T[size];
    iter = buffer;
  }

  virtual ~FixedStacked() {
    delete [] buffer;
  }
}

virtual keyword will be discussed later.

buffer is a pointer that points into an array in the heap. If buffer is not deleted, this memory would leak. By deleting the buffer in destructor, it's ensured that the buffer is freed when the FixedStacked object is being destroyed, either by ending of scope, or by calling delete operator.

stack variable

void test()
{
  FixedStacked<int> stack;
  stack.push(50);
  stack.push(100);
} // <<stack is destroyed, the destructor would be called here.

heap variable

FixedStacked<int> * stack = new FixedStacked<int>();
delete stack; //<-the destructor is called here.

In any subclasses, the destructor of super classes will be called implicitly in the reverse order.

#include <iostream>

class A {
    public:
    virtual ~A() {
        std::cout<< "A's Destructor"<< std::endl;
    }
};

class B {
    public:
    virtual ~B() {
        std::cout<< "B's Destructor"<< std::endl;
    }
};

class C: public A, public B {
    public:
    virtual ~C(){
        std::cout<< "C's Destructor"<< std::endl;
    }
};

int main() {
  C c;
}
C's Destructor
B's Destructor
A's Destructor

Virtual Function & Polymorphism

virtual function is function which allows itself to be overridden by the sub class. This is used to allow polymorphism.

#include <iostream>

class A {
public:
  virtual void print() {std::cout<<"A";};
};

class B : public A {
public:
  virtual void print() {std::cout<<"B";}
};

int main()
{
  A *a = new B;
  a->print();
}
B

Overriding non-virtual function is actually possible in C++, but it would works unexpectedly when using it in the polymorphic way.

#include <iostream>

class A {
public:
  void print() {std::cout<<"A";};
};

class B : public A {
public:
  void print() {std::cout<<"B";}
};

int main()
{
  A *a = new B;
  a->print();
}
A

Polymorphism also applies to reference as well as pointers.

#include <iostream>

class A {
public:
  virtual void print() {std::cout<<"A";};
};

class B : public A {
public:
  virtual void print() {std::cout<<"B";}
};

int main()
{
  B b;
  A &a = b;

  a.print();
}
B

A virtual function can be masked as pure by putting = 0 at the function declaration. Pure virtual function makes the class non-instantiable. This class then will be used via its subclasses which are required to have this function overridden. Classes contains pure virtual functions are called abstract class.

class A {
public:
  virtual void print() = 0;
};

class B : public A {
public:
  void print() {std::cout<<"Concrete Class";}
};

A a; // build error. can't instantiate an abstract class.
A *a = new A(); // build error. can't instantiate an abstract class.
A *b = new B(); // OK

Operator overloading

In C++, most operator like mathematic operator (+ - * /), assignment (=), subscription ([]), can be overriden. This will not be discussed in this session.

Lambda Expression

In C++, it is possible to create an object that acts like a function. This can be useful when a function/method needs to be flexible in terms of behavior.

Suppose we have a class like.

class Entity {
public:
  std::string GetName() const;
  std::string GetLastName() const;
}

And we want to sort an array of `Entity` based on either by name or by last name. Without providing the comparator, a function object which compare two objects, two function would be needed to provided such ability.

```cpp
Entity entities[];
std::sort(std::begin(entities), std::end(entities), sortByName);
std::sort(std::begin(entities), std::end(entities), sortByLastname);

There are many ways to create function object (or functor). Lambda Expression is the easiest way to create them. By using lambda the code above can be completed as follow;

auto sortByName = [](const Entity& e1, const Entity& e2) {return e1.GetFirstName() < e2.GetFirstName();};
auto sortByLastName = [](const Entity& e1, const Entiy & e2) {return e1.GetLasName() < e2.GetLastName()};
Entity entities[];
std::sort(std::begin(entities), std::end(entities), sortByName);
std::sort(std::begin(entities), std::end(entities), sortByLastname);

Lambda can be in-place at th function call as well.

auto sortByName = [](const Entity& e1, const Entity& e2) {return e1.GetFirstName() < e2.GetFirstName();};
auto sortByLastName = [](const Entity& e1, const Entiy & e2) {return e1.GetLasName() < e2.GetLastName()};
Entity entities[];
std::sort(std::begin(entities), std::end(entities), [](const Entity& e1, const Entity& e2) {return e1.GetFirstName() < e2.GetFirstName();});
std::sort(std::begin(entities), std::end(entities), [](const Entity& e1, const Entiy & e2) {return e1.GetLasName() < e2.GetLastName()});

Standard Library/Standard Template library

Unlike Java which comes with gigantic class library, C++ provides only a small set of Standard Library. Apart from C++, C library is also available, which most of the functionality are also provided by C++ standard library. If possible, avoid using C library.

*C++ Standard Library provides a set of containers, I/O, and some other functionality like string processing and exception.

Many of C++ Standard Library are template-based, which make it extremely versatile. These template-based library is also call Standard Template Library or STL.

Here's some classes provided by the Standard Library. All identifier are contains inside the std namespace

  • std::string
  • std::vector
  • std::map
  • std::sort

This discussion does not cover these library in details. Please see the reference at the end of the document.

Reference

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment