Skip to content

Instantly share code, notes, and snippets.

@Micrified
Last active November 26, 2019 13:38
Show Gist options
  • Save Micrified/456c53a75bc271ab63c2d039ff101ec2 to your computer and use it in GitHub Desktop.
Save Micrified/456c53a75bc271ab63c2d039ff101ec2 to your computer and use it in GitHub Desktop.
C++

C++

File headers

Begin all files by including your headers, possibly after whatever comment banner you might want to include. To avoid an annoying implicit namespace warning, make sure to declare you are using the std namespace after the #include directives. Example:

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

using namespace std;

Casting types

Standard Cast

Format for a standard cast (e.g. double to `int):

static_cast<type>(expression)

Examples include:

int n = static_cast<int>(3.14);
n += static_cast<int>(9.1);

Const Modifier Cast

The const cast does away with the const type modifier.

const_cast<type>(expression)

Examples include:

int len (char *string);

int main () {
	const char str[] = "Foo";

	// ERR: Cannot pass const to function without const!
	cout << "String Length = " << len(str) << endl;

	// OK: Casting to bypass (only if you know it won't modify it)
	cout << "String Length = " << len(const_cast<char *>(str)) << endl;
}

Interpretation Cast

Allows you to reinterpret information. Example might be you want to convert a double to a byte sequence.

double d = 3.14;

// Cast to a byte array
char *ptr = reinterpret_cast<char *>(&d);

Polymorphic (Dynamic) Cast

This is for use with objects, where you can cast an object to another member of it's class hierarchy:

dynamic_cast<type>(expression)

Using C Functions

To use functions from the C library, you usually have to declare them like so:

extern "C" void *xmalloc (unsigned size);

Alternatively, you can just stuff everything you want to use inside a block as demonstrated below:

extern "C" {

	// Include some headers too!
	#include <header.h>

	void *xmalloc (unsigned size);

	// More function declarations ...
}

Preprocessor Header

Here is a good way to declare hybrid C/C++ header files

#ifndef FOO_H
#define FOO_H

#ifdef __cplusplus
extern "C" {
#endif

// All C declarations go in here
extern void *xmalloc (unsigned size);

#ifdef __cplusplus
}
#endif

#endif

Note that while C files usually end their headers with a .h suffix, C++ files don't typically do this at all. As an example, consider the stream class

#include <iostream>

Of course, you can still add a .h extension to your header files if you wish. Nobody is stopping you.

Overloading

In C++ you can overload a function with the same name using different parameter types. See some examples below:

#include <stdio.h>

void show (int val) {
	printf("Integer: %d\n", val);
}

void show (double val) {
	printf("Double: %lf\n", val);
}

void show (char *val) {
	printf("String: %s\n", val);
}

int main () {
	show(12);
	show(3.1415);
	show("Hey!\n");
}

// Can be confusing. Is show(0) going to
// 1. Use int, because 0 is an int
// 2. Use char *, because its a null pointer
// It actually will use (1), but just be aware!

Default Arguments

C++ allows functions to be given default arguments. This is very handy. While you may ommit parameters from the end in order to use their defaults. You cannot specify some while leaving any before it 'blank'. See the examples below for this:

#include <stdio.h>

void show (char *str = "Nothing!\n") {
	printf(str);
}

void main () {
	show("Input given!\n");
	show();
}

Multiple argument example:

void foo (int a = 4, int b = 2) {
	// ...
}

Rules when using default arguments

int main () {
	foo();   // Ok
	foo(1);  // Ok (goes to first arg)
	foo(,2); // NOT ok!
}

You want to put the defaults in your header file so the compiler can easily find them. See the layout example below:

// Header file ...
extern void foo (int a = 1, int b = 2);

// Source file ...
void foo (int a, int b) {
	// ...
}

Type Definitions

The typedef keyword is available in C++, but it isn't really needed. As an example, consider a C-style struct as shown below:

// C
struct bar {
	int a;
	double d;
	char str[42];
}

Now, you normally need to always refer to this as struct bar. However, you don't need to in C++. You can just use bar without having to explicitly typedef' it.

// C++
bar.d = 3.14; // <- Allowed, don't need typedef struct bar bar

Although not really exclusive to C++, you can handily throw functions into structs. The following is an example:

struct canvas {
	int width, height;
	void draw (void);     /// <-- Only a declaration though, must be defined elsewhere
}

Note: The size of the structure canvas is actually just two integers. Functions are not really included in the structure on compile time. Examples of how to use struct-functions are below:

// How to call?
canvas myCanvas;
myCanvas.draw();

// OR when you have a struct pointer
canvas *myCanvas;
myCanvas->draw();

Syntactic Differences

One new operator is the scope resolution operator. This can be used in situations where a global variable exists with the same name as a local variable

#include <stdio.h>

int counter = 60;

int main () {
	for (register int counter = 1; counter > 0; counter++) {

		// Here we see that the :: operator refers to a global scope for resolving counter
		printf("%d %d\n", ::counter, counter);
	}

	return 0;
}

Cout, Cin, and Cerr

Here we describe what C++ calls streams, for IO purposes:

  • Cout: Referred to as cout, this is basically stdout
  • Cin: Referred to as cin, this is basically stdin
  • Cerr: Referred to as cerr, this is basically stderr

You can read or write to streams using the > and < operator. This is called the extraction operator:

#include <iostream>
using namespace std;


// cin, cout, and cerr are objects of a given class. 

 int main () {
 	int ival;
 	char sval[30];

 	// Print line to standard out, append with endl to signify line termination
 	cout << "Enter a number: " << endl;

 	// Automatically parses an integer, and cleverly distinguishes floats, removes ws, etc
 	cin >> ival;

 	cout << "And now a string: " << endl;

 	cin >> sval;

 	// Output depends on the type in the stream. You can see that
 	// numbers are correctly printed and so are strings based on
 	// their type
 	cout << "Number is " << ival << endl
 	     << "String is " << sval << endl;
 }

You don't have to use streams, and the standard library printf and scanf from C are still available. However there are some considerations that might make one option or the other better in situations. For instance:

  1. Insertion and extraction from streams is more type safe. Argument checking is supplied by the compiler since you pass types, whereas a printf cannot always be analyzed.

  2. Format strings need to be parsed at runtime, which adds overhead. The streams do not have to be, which is an advantage to have

  3. Overloaded operators can be quite convenient (need to see more myself)

  4. Iostreams are extensible. So you can add functionality using inheritance since it is a class. This is discussed in more detail later.

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