TABLE OF CONTENTS (HIDE)

C++ Programming Language

More Basics

This chapter is written for completeness. Novices may slip this chapter.

*Enumeration (enum)

An enum is a user-defined type of a set of named constants, called enumerators. An enumeration define the complete set of values that can be assigned to objects of that type. For example,

enum Color {
   RED, GREEN, BLUE
} myColor;        // Define an enum and declare a variable of the enum
......
myColor = RED;    // Assign a value to an enum
Color yourColor;
yourColor = GREEN;

The enumerators are represented internally as integers. You have to use the names in assignment, not the numbers. However, it will be promoted to int in arithmetic operations. By default, they are running numbers starting from zero. You can assigned different numbers, e.g.,

enum Color {
   RED = 1, GREEN = 5, BLUE
};

To print the enumerator names, you may need to define a array of string, indexed by the enumerator numbers.

Example: Deck of Card

[TODO]

*Structure (struct)

A struct (structure) is a user-defined data structure that can be used to hold a set of variables of different types (called members). To defined a struct:

struct StructName {
   type1 var1;
   type2 var2;
   .......
};  // need to terminate by a semi-colon

struct is an intermediate step towards Object-oriented Programming (OOP). In OOP, we use class that is an user-defined structure containing both data members and member functions.

Example: struct Point
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* Testing struct (TestStruct.cpp) */
#include <iostream>
using namespace std;
 
struct Point {
   int x;
   int y;
};
 
int main() {
   Point p1 = {3, 4};  // declare and init members
   cout << "(" << p1.x << "," << p1.y << ")" << endl;  // (3,4)
 
   Point p2 = {};      // declare and init numbers to defaults
   cout << "(" << p2.x << "," << p2.y << ")" << endl;  // (0,0)
   p2.x = 7;
   p2.y = 8;
   cout << "(" << p2.x << "," << p2.y << ")" << endl;  // (7,8)
   return 0;
}

Notes:

  • You can declare and initialize an instance of struct as shown in Line 11 and Line 14.
  • To access members of a struct, use the dot (.) operator.
Example: struct Person

A struct can contains members of different types.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* Testing struct (TestStructPerson.cpp) */
#include <iostream>
#include <string>
using namespace std;
 
struct Person {
   string name;
   int age;
   double height;
   double weight;
};
 
int main() {
   Person peter = {"Peter Jone", 18, 180.5, 70.5};
   cout << "Name: " << peter.name << endl;
   cout << "Age: " << peter.age << endl;
   cout << "Height: " << peter.height << endl;
   cout << "weight: " << peter.weight << endl;
   return 0;
}
Example: structs Point and Rectangle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*
 * Testing struct (TestStruct.cpp)
 */
#include <iostream>
using namespace std;
 
struct Point {
  int x, y;
};
 
struct Rectangle {
  Point topLeft;
  Point bottomRight;
};
 
int main() {
   Point p1, p2;
   p1.x = 0;  // p1 at (0, 3)
   p1.y = 3;
   p2.x = 4;  // p2 at (4, 0)
   p2.y = 0;
 
   cout << "p1 at (" << p1.x << "," << p1.y << ")" << endl;
   cout << "p2 at (" << p2.x << "," << p2.y << ")" << endl;
 
   Rectangle rect;
   rect.topLeft = p1;
   rect.bottomRight = p2;
 
   cout << "Rectangle top-left at (" << rect.topLeft.x
        << "," << rect.topLeft.y << ")" << endl;
   cout << "Rectangle bottom-right at (" << rect.bottomRight.x
        << "," << rect.bottomRight.y << ")" << endl;
 
   return 0;
}
p1 at (0,3)
p2 at (4,0)
Rectangle top-left at (0,3)
Rectangle bottom-right at (4,0)
C++ structs

C++ evolves the C's structs into OOP classes. The legacy C's structs contain public data members. C++ structs, like classes, may contain access specifiers (public, private, protected), member functions, constructor, destructor and support inheritance. The only difference for C++'s structs and classes is the struct's members default to public access while class members default to private access, if no access specifier is used. Also, C++ structs default to public-inheritance whereas classes default to private-inheritance.

*Bit-wise Operations

Bit-wise Logical Operations

Bit-wise operators perform operation on one or two operands on a bit-by-bit basis.

Operator Description Usage
& Bit-wise AND expr1 & expr2
| Bit-wise OR expr1 | expr2
! Bit-wise NOT !expr
^ Bit-wise XOR expr1 ^ expr2

Compound operator &=, |= and ^= are also available.

Bit-Shift Operations

Bit-shift operators perform left or right shift on an operand by a specified number of bits. Left-shift is padded with zeros. Right-shift may padded with zero or sign-bit, depending on implementation.

Operator Usage Description
<< operand << number Left-shift and padded with zeros
>> operand >> number Right-shift

*More on Functions

Inline Functions

Function call has its overhead (handling the argument, managing function stack, branch and return). For simple and short functions, you may use inline functions to remove the function call overhead. The keyword inline (before the function's return-type) suggest to the compiler to "expand" the function code in place, instead of performing a function call. For example,

1
2
3
4
5
6
7
8
9
10
11
12
13
//  TestInlineFunction.cpp
#include <iostream>
using namespace std;
 
inline int max(int n1, int n2) {
   return (n1 > n2) ? n1 : n2;
}
 
int main() {
   int i1 = 5, i2 = 6;
   cout << max(i1, i2) << endl;  // inline request to expand to (i1 > i2) ? i1 : i2
   return 0;
}

The compiler may expand line 11 as:

cout << (i1 > i2) ? i1 : i2 << endl;

The expanded statement is faster than invoking a function call. The trade-off is bigger code size.

Inline is just a recommendation. Compiler may not support or may ignored the recommendation.

Inline Function vs. #define Macro

In C, you can use the #define preprocessor directive to define a macro with argument, which would then be expanded during pre-processing. For example,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* Test #define macro (TestMacro.cpp) */
#include <iostream>
using namespace std;
 
#define SQUARE(x) x*x     // Macro with argument
 
inline int square(int x) { return x*x; }  // inline function
 
int main() {
   cout << SQUARE(5) << endl;  // expand to 5*5 (25)
   int x = 2, y = 3;
   cout << SQUARE(x) << endl;  // expand to x*x (4)
 
   // Problem with the following macro expansions
   cout << SQUARE(5+5) << endl;   // expand to 5+5*5+5 - wrong answer
   cout << square(5+5) << endl;   // Okay square(10)
   cout << SQUARE(x+y) << endl;   // expand to x+y*x+y - wrong answer
   cout << square(x+y) << endl;   // Okay
      // can be fixed using #define SQUARE(x) (x)*(x)
 
   cout << SQUARE(++x) << endl;   // expand to ++x*++x (16) - x increment twice
   cout << x << endl;             // x = 4
   cout << square(++y) << endl;   // Okay ++y, (y*y) (16)
   cout << y << endl;             // y = 4
}
 

Inline function is preferred over macro expansion, as seen from the above example.

Ellipses (...)

Ellipses (...) can be used as the last parameter of a function to denote zero or more arguments of unknown type. The compiler suspends type checking for these parameters. For example,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*
 *  TestEllipses.cpp
 */
#include <iostream>
#include <cstdarg>
using namespace std;
 
int sum(int, ...);
 
int main() {
   cout << sum(3, 1, 2, 3) << endl;       // 6
   cout << sum(5, 1, 2, 3, 4, 5) << endl; // 15
 
   return 0;
}
 
int sum(int count, ...) {
   int sum = 0;
 
   // Ellipses are accessed thru a va_list
   va_list lst;  // Declare a va_list
   // Use function va_start to initialize the va_list,
   // with the list name and the number of parameters.
   va_start(lst, count);
   for (int i = 0; i < count; ++i) {
      // Use function va_arg to read each parameter from va_list,
      // with the type.
      sum += va_arg(lst, int);
   }
   // Cleanup the va_list.
   va_end(lst);
 
   return sum;
}

Scope Resolution Operator

The symbol :: is known as scope resolution operator. If a global variable is hidden by a local variable of the same name (of course not recommended), you could use the scope resolution operator to retrieve the hidden global variable. For example,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//  TestScopeResolutionOperator.cpp
#include <iostream>
using namespace std;
 
// Global variable
int x = 5;
 
int main() {
   // A local variable having the Same name as a global variable,
   // which hides the global variable
   float x = 55.5f;
 
   // Local
   cout << x << endl;
 
   // Use unary scope resolution operator to retrieve the global variable
   cout << ::x << endl;
 
   return 0;
}

Command-Line Arguments

You may include arguments in the command-line, when running a program, for example,

$ gcc -o test test.cpp

"-o test test.cpp" are called command-line arguments. Each of the argument is a string, all the arguments form a string array, and passed into the main() function of the program.

To process command-line argument, the main() function shall use this header:

int main(int argc, char *argv[]) { ...... }

The second parameter char *argv[] captures the string array, while the first parameter capture the size of the array, or the number of arguments.

For example,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
 *  Arithmetic.cpp
 *  Usage: Arithmetic num1 num2 operator
 */
#include <iostream>
#include <cstdlib>
using namespace std;
 
int main(int argc, char *argv[]) {
 
   if (argc != 4) {
      cout << "Usage: Arithmetic num1 num2 operator" << endl;
      exit(1);
   }
 
   int operand1 = atoi(argv[1]);  // Parse string to int
   int operand2 = atoi(argv[2]);  // Parse string to int
   char op = argv[3][0];          // Extract first character only
 
   switch (op) {
      case '+':
         cout << operand1 << " + " << operand2 << " = " << operand1 + operand2 << endl;
         break;
      case '-':
         cout << operand1 << " - " << operand2 << " = " << operand1 - operand2 << endl;
         break;
      case '*':
         cout << operand1 << " * " << operand2 << " = " << operand1 * operand2 << endl;
         break;
      case '/':
         cout << operand1 << " / " << operand2 << " = " << operand1 / operand2 << endl;
         break;
      default:
         cout << "Unknown operator" << endl;
         exit(1);
   }
 
   return 0;
}

*More on Preprocessor Directives

A preprocessor directive begins with # (e.g., "#include <stdio.h>", "#define PI 3.14256295"). When you compile a C/C++ program, these commands will be pre-processed to produce the source file for the actual compilation (e.g., to include a specific header file in this program or to define a certain macro substitution).

A preprocessor command is NOT a C statement, and it does NOT end with a semi-colon.

#include: #include is most commonly-used to include a header file into this source file for subsequent compilation. The syntax is as follows:

// Syntax
#include header_file
   
// Examples   
#include <stdio.h>      // Search the compiler's include paths
#include "myHeader.h"   // Search the current directory first

#define, #undef: #define can be used to define a macro. When the macro pattern appears subsequently in the source codes, it will be replaced or substituted by the macro's body. Macro may take parameters. You can use #undef to un-define a macro.

// Define a macro substitution
#define marco_name macro_value
   
// Define a macro with parameters
#define marco_name(args) marco_definition
   
   
   
   
// Un-define a macro name
#undef marco_name
// Example   
#define PI 3.14159256
   
// Example
#define square(x)  (x*x)
#define max(x,y)   ((x > y) ? x : y)
#define REP(i,n)   for (i = 0; i < n; ++i)
#define FOR(i,a,b) for (i = a; i < b; ++i)
   
// Example
#undef PI

Two common mistakes when using a #define command for macro substitution are:

#define PI = 3.14159265   // 1
#define PI 3.14159265;    // 2

In case 1, the macro name "PI" is defined to be "= 3.14159265" (with the leading '=' sign). In case 2, it is "3.14159265;" (with a tailing ';'). These will certainly trigger errors in your program during the actual compilation.

#define is also commonly-used in define a macro name with an empty body, to be used in the conditional directives such as #ifdef and #ifndef.

#ifdef, #ifndef, #if, #elif, #endif: Conditional directives can be used to control the sections of program send for compilation:

// if the marco_name is defined
#ifdef macro_name
  ......
#endif
  
// if the macro_name is NOT defined
#ifndef macro_name
  ......
#endif
  
// conditional if else
#if expression
  ......
#elif condition
  ......
#else
  ......
#endif
// Examples
  
  
  
  
  
  
   
  
  

#if 1 (always true), #if 0 (always false)
  
  
  
 
 
 

For example,

#ifndef SIZE
#define SIZE 1000
#endif
......

For example, in a header file (e.g., myHeader.h), the following directive are often used to prevent this header from included more than once in a source file.

#ifndef __MY_HEADER
#define __MY_HEADER
#endif
......

#pragma: The directive #pragma can be used to direct compiler for system-dependent information. A common usage is:

#ifdef _MSC_VER                        // This pragma comment works on MS Visual C++ only, NOT Gcc
#pragma comment(lib, "opengl32.lib")   // Direct compiler to include this library, same as -l command-line option
#endif

Others: Other directives include #errors and #line, which are not commonly used.

Link to "C++ References & Resources"