Skip to content

Module00

Marouane BENBAJJA edited this page Apr 14, 2023 · 2 revisions

Table of content

Namespaces

  • namespaces provides a method for preventing name conflicts in large projects. It allows you to group symbols that are related together

  • All the entities included in the C++ standard library are included in the std namespace

  • However, using namespace std is considered bad practice, as it imports the entirety of the std namespace into the current namespace of the program. It's better to use scope resolution operator to access the specific entity (like std::cout)

int	gl_var = 1;
int	f(void) {return 2;}

namespace	Foo {
	int	gl_var = 3;
	int	f(void) {return 4;}
}

namespace	Bar {
	int 	gl_var = 5;
	int	f(void) {return 6;}
}

// use namespace aliasing to keep a shorter and more expressive syntax
// :: scope resolution operator
namespace 	Muf = Bar;

int	main(void){
	printf("gl_var:			[%d]\n", gl_var);
	printf("f():			[%d]\n\n", f());

	//exactly the same as above (global scope)
	printf("::gl_var:		[%d]\n", ::gl_var);
	printf("::f():			[%d]\n\n", ::f());

	printf("Foo::gl_var:		[%d]\n", Foo::gl_var);
	printf("Foo::f():		[%d]\n\n", Foo::f());

	printf("Bar::gl_var:		[%d]\n", Bar::gl_var);
	printf("Bar::f():		[%d]\n\n", Bar::f());

	printf("Muf::gl_var:		[%d]\n", Muf::gl_var);
	printf("Muf::f():		[%d]\n\n", Muf::f());

	return (0);
}

Output

gl_var:			[1]
f():			[2]

::gl_var:		[1]
::f():			[2]

Foo::gl_var:		[3]
Foo::f():		[4]

Bar::gl_var:		[5]
Bar::f():		[6]

Muf::gl_var:		[5]
Muf::f():		[6]

Streams

  • Streams acts as an intermediaries between the programs and the actual IO devices. It frees the programmers from handling the actual devices

  • << >> redirects the flow to or from a stream (like std::cout or std::cin)

  • Object std::endl handles newline character on different OS

#include <iostream>

int main(void)
{
	char buff[512];

	std::cout << "Hello World!" << std::endl;
	std::cout << "Input a word: ";
	std::cin >> buff;
	std::cout << "You entered: [" << buff << "]" << std::endl;
	return 0;
}

Output

Hello World!
Input a word: xx
You entered: [xx]

Class & Instance

  • C++ is an object-oriented programming language. An object is created from a class. Everything in C++ is associated with classes and objects, along with its attributes(variables) and methods(functions)

  • A class is a user-defined data type. It works as an object constructor, or a blueprint for creating objects. A class is static and it's a model which objects are built on. Instance is the dynamic part and it's an instantiation of a class

  • Constructors and destructors are special member functions of classes that are used to construct and destroy class objects.
    • Constructor may involve memory allocation and initialization for objects. Destructor may involve cleanup and deallocation of memory for objects
    • Like other member functions, constructors and destructors are declared within a class declaration. They can be defined inline or external to the class declaration
    • Constructor and destructor don't have a return type
    • The compiler automatically calls constructors when defining class objects and calls destructors when class objects go out of scope.

An example of Class

  • Sample.class.hpp
#ifndef SAMPLE_CLASS_H
# define SAMPLE_CLASS_H

class	Sample
{
public:

	Sample(void); // CPP use the name of the class for constructor
	~Sample(void); // and the name of the class for destructor
}; // don't forget to end with semicolon

#endif
  • Sample.class.cpp
#include <iostream>
#include "Sample.class.hpp"

Sample::Sample(void)
{
	std::cout << "Constructor called" << std::endl;
	return ;
}

Sample::~Sample(void)
{
	std::cout << "Destructor called" << std::endl;
	return ;
}
  • main.cpp
#include "Sample.class.hpp"

int	main(void)
{
	// the line below instantiate an instance
	// code in constructor will also run (to initialise data for instance)
	Sample	instance;
	return (0);
	// destructor is called when the instance goes out of scope
}

Output

Constructor called
Destructor called

Member Attributes & Functions

  • A member function of a class is a function that has its definition or its prototype within the class definition like any other variable. It operates on any object of the class of which it is a member, and has access to all the members of a class for that object.

  • Member attribute is a variable you have in your class, and you may use from an instance.

An example of class and member functions (source link)

non-member Attributes & Functions

  • Member attributes / functions are present inside of a class. It means that the class needs to be instantiated, in order to use this attribute / function. Each attribute will be different in each instance

  • Non member variables and functions exist at the class level, and not at the instance level

  • Another terminology:

    • Instance variables / functions refers to member attributes / functions
    • Class variables / functions refers to non member attributes / functions

An algorithm on whether to choose member function or non member function, created by Scott Meyers (source link)

if (f needs to be virtual)
   make f a member function of C;
else if (f is operator>> or operator<<)
   {
   make f a non-member function;
   if (f needs access to non-public members of C)
      make f a friend of C;
   }
else if (f needs type conversions on its left-most argument)
   {
   make f a non-member function;
   if (f needs access to non-public members of C)
      make f a friend of C;
   }
else if (f can be implemented via C public interface)
   make f a non-member function;
else
   make f a member function of C;

Example of non member attributes / functions

  • Sample.class.hpp
#ifndef SAMPLE_CLASS_H
# define SAMPLE_CLASS_H

class Sample {

public:

	Sample(void);
	~Sample(void);

	// non member function
	// when the function does not have anything to do with the other class
	// functions, but you want it to be part of the class
	static int getNbInst(void);

private:

	// non member attribute
	// number of instances: this information only makes sense at the class level
	static int _nbInst;

};

#endif
  • Sample.class.cpp
#include <iostream>
#include "Sample.class.hpp"

Sample::Sample(void)
{
	std::cout << "Constructor called" << std::endl;
	Sample::_nbInst += 1;

	return;
}

Sample::~Sample(void)
{
	std::cout << "Destructor called" << std::endl;
	Sample::_nbInst -= 1;
	return;
}

// With member function, CPP will pass as a parameter, an instance of your class
// so this-> can be used

// this pointer is not passed for a non member function, so you can't
// use this-> here
int	Sample::getNbInst(void)
{
	return Sample::_nbInst;
}

// this is the only way to initialise a non member attribute (static attribute)
// this is initialised without an instance being created
int	Sample::_nbInst = 0;
  • main.cpp
#include <iostream>
#include "Sample.class.hpp"

void f0(void)
{
	Sample instance;

	std::cout << "Number of instances = " << instance.getNbInst() << std::endl;

	return;
}

void f1(void)
{
	Sample instance;

	std::cout << "Number of instances = " << instance.getNbInst() << std::endl;
	f0();

	return;
}

int main(void)
{
	std::cout << "Number of instances = " << Sample::getNbInst() << std::endl;
	f1();
	std::cout << "Number of instances = " << Sample::getNbInst() << std::endl;

	return 0;
}

Output

Number of instances = 0
Constructor called
Number of instances = 1
Constructor called
Number of instances = 2
Destructor called
Destructor called
Number of instances = 0

this Pointer (the self keyword)

  • In C++, each object gets its own copy of data members and all objects share a single copy of member functions. To refer to your current instance, use the special instance pointer this.

  • One member function only has one copy and is used by multiple objects, how are the proper data members accessed and updated? The compiler supplies an implicit pointer along with the names of the functions as 'this'

Example of member attributes, functions & this pointer

  • Sample.class.hpp
#ifndef SAMPLE_CLASS_H
# define SAMPLE_CLASS_H

class Sample
{
	public:

		int	foo;

		Sample(void);
		~Sample(void);

		void	bar(void);
};

#endif
  • Sample.class.cpp
#include <iostream>
#include "Sample.class.hpp"

Sample::Sample(void)
{
	std::cout << "Constructor called" << std::endl;

	this->foo = 42;
	std::cout << "this->foo: " << this->foo << std::endl;

	// it is possible to call a function from an instance pointer
	this->bar();
	return;
}

Sample::~Sample(void)
{
	std::cout << "Destructor called" << std::endl;
	return;
}

void	Sample::bar(void)
{
	std::cout << "Member function bar called" << std::endl;
	return;
}
  • main.cpp
#include <iostream>
#include "Sample.class.hpp"

int	main(void)
{
	Sample	instance;

	return (0);
}

Output

Constructor called
this->foo: 42
Member function bar called
Destructor called

Initialization List

  • Initialization list is used in initializing the data members of a class. The list of members to be initialized is indicated with constructor as a comma-separated list followed by a colon

Example of initialization list

  • Sample.class.hpp
#ifndef SAMPLE_CLASS_H
# define SAMPLE_CLASS_H

class Sample {

public:
	//attributes
	char a1;
	int a2;
	float a3;

	Sample(char p1, int p2, float p3);
	~Sample(void);
};

#endif
  • Sample.class.cpp
#include <iostream>
#include "Sample.class.hpp"

//initialise the attributes in a class
Sample::Sample(char p1, int p2, float p3) : a1(p1), a2(p2), a3(p3)
{
	std::cout << "Constructor called" << std::endl;
	std::cout << "this->a1 = " << this->a1 << std::endl;
	std::cout << "this->a2 = " << this->a2 << std::endl;
	std::cout << "this->a3 = " << this->a3 << std::endl;
}

Sample::~Sample(void) {
	std::cout << "Destructor called" << std::endl;
	return;
}
  • main.cpp
#include <iostream>
#include "Sample.class.hpp"

int	main(void)
{
	Sample	instance('a', 42, 4.2f);

	return (0);
}

Output

Constructor called
this->a1 = a
this->a2 = 42
this->a3 = 4.2
Destructor called

Const

  • Variables declared with const added become constants and cannot be altered by the program

  • const data members must be initialized using initialization list. No memory is allocated separately for const data member. It is folded in the symbol table due to which we need to initialize it

  • const data members is also a copy constructor. We don't need to call the assignment operator which means we are avoiding one extra operation

  • The more your code is const and read only, the more your code will be robust in the long term

Example of const

  • Sample.class.hpp
#ifndef SAMPLE_CLASS_H
# define SAMPLE_CLASS_H

class Sample
{
public:

	float const pi;
	int qd;

	Sample(float const f);
	~Sample(void);

	// const between ) & ; -> the instance of the class will never be altered
	void bar(void) const;
};

#endif
  • Sample.class.cpp
#include <iostream>
#include "Sample.class.hpp"

// it's not assigning f value to pi attribute
// it's initialising pi attribute to the f value
Sample::Sample(float const f) : pi(f), qd(42) {

	std::cout << "Constructor called" << std::endl;
	return;
}

Sample::~Sample(void) {

	std::cout << "Destructor called" << std::endl;
	return;
}

// this is central to CPP
// define const when your member function should never be changed in the \
// current instance of your class
void	Sample::bar(void) const {

	std::cout << "this->pi = " << this->pi << std::endl;
	std::cout << "this->qd = " << this->qd << std::endl;

	// this assignment below won't work, as it's a const function
	// this->qd = 0;
	return;
}
  • main.cpp
#include <iostream>
#include "Sample.class.hpp"

int main() {

	Sample instance(3.14f);

	instance.bar();

	return (0);
}

Output

this->pi = 3.14
this->qd = 42
Destructor called

Getters

  • An accessor is a member function that allows someone to retrieve the contents of a protected data member
    • The accessor must have the same type as the returned variable
    • The accessor does not need to have arguments
    • A naming convention must exist, and the name of the accessor must begin with the get prefix
  • In practice, all attributes of a class are private. Getters are the interface between the user and private attributes, to be sure that the values are always correct.

  • They usually have the prefix of get and set. Getter can only access in the read-only mode and won't alter the content of the class. It allows you to have some control over what to give to the user. It also sets control to the user's input and make sure that they make sense

Example of getter

  • Sample.class.hpp
#ifndef SAMPLE_CLASS_H
# define SAMPLE_CLASS_H

class Sample
{
public:

	Sample(void);
	~Sample(void);

	int	getFoo(void) const;
	void setFoo(int v);

private:

	int	_foo;
};

#endif
  • Sample.class.cpp
#include <iostream>
#include "Sample.class.hpp"

Sample::Sample(void)
{
	std::cout << "Constructor called" << std::endl;

	this->setFoo(0);
	std::cout << "this->getFoo() = " << this->getFoo() << std::endl;

	return;
}

Sample::~Sample(void)
{
	std::cout << "Destructor called" << std::endl;
	return;
}

// grant only read-only access to the attribute
int	Sample::getFoo(void) const
{
	// return a copy of the _foo attribute
	return this->_foo;
}

// this way allows the user to change the _foo attribute
void	Sample::setFoo(int v)
{
	// don't allow _foo to have a negative value
	if (v >= 0)
		this->_foo = v;
	return;
}
  • main.cpp
#include <iostream>
#include "Sample.class.hpp"

int	main(void)
{
	Sample	instance;

	instance.setFoo(42);
	std::cout << "instance.getFoo() = " << instance.getFoo() << std::endl;
	instance.setFoo(-42);
	std::cout << "instance.getFoo() = " << instance.getFoo() << std::endl;

	return (0);
}

Output

Constructor called
this->getFoo() = 0
instance.getFoo() = 42
instance.getFoo() = 42
Destructor called

Class vs Struct

  • A class can also contain functions (called methods in C++). The member attributes and methods are hidden from the outside world, unless their declaration follows a public label

  • Special methods (constructor and destructor) in C++. They run automatically when an object (an instance of the class) is created and destroyed

  • Operators to work on the new data type can be defined using special methods (member functions)

  • One class can be used as the basis for the definition of another (inheritance)

  • Declaring a variable of the new type (an object) requires just the name of the class. The keyword class is not required

  • struct and class work almost the same way in C++. The difference is that the default accessibility / scope of member variables and methods is private in a class, while it's public in a struct. The use of class is generally preferred

Clone this wiki locally