In C, the names of the Standard Library header files usually end in .h, such as <stdio.h>, and namespaces are not used. In C++, the .h suffix is omitted for Standard Library headers, such as <iostream>, and everything is defined in the std namespace or a sub-namespace of std.
The Standard Library headers from C still exist in C++ but in two versions:
The new and recommended versions without a .h suffix but with a c prefix. These versions put everything in the std namespace (for example,<cstdio>).
The old versions with the .h suffix. These versions do not use namespaces (for example, <stdio.h>).
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
nested namespaces
namespace MyLibraries::Networking::FTP {
/* ... */
}
To get the size of a stack-based C-style array, you can use the C++17 std::size() function
unsigned int arraySize = std::size(myArray);
To get the number of elements in a stack-based array, you divide the size in bytes of the array by the size in bytes of the first element
unsigned int arraySize = sizeof(myArray) / sizeof(myArray[0]);
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
std::array
The arrays discussed in the previous section come from C, and still work in C++. However, C++ has a special type of fixed-size container called std::array, defined in the <array> header file. It’s basically a thin wrapper around C-style arrays.
array<int, 3> arr = {9, 8, 7};
cout << "Array size = " << arr.size() << endl;
cout << "2nd element = " << arr[1] << endl;
Both the C-style arrays and the std::arrays have a fixed size, which must be known at compile time. They cannot grow or shrink at run time.
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
std::vector
A vector is dynamic, meaning that elements can be added and removed at run time
// Create a vector of integers
vector<int> myVector = { 11, 22 };
// Add some more integers to the vector using push_back()
myVector.push_back(33);
myVector.push_back(44);
// Access elements
cout << "1st element: " << myVector[0] << endl;
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Structured Bindings
C++17 introduces the concept of structured bindings. Structured bindings allow you to declare multiple. variables that are initialized with elements from an array, struct, pair, or tuple.
Note that you have to use the auto keyword for structured bindings.
The number of variables declared with the structured binding has to match the number of values in the expression on the right.
struct Point { double mX, mY, mZ; };
Point point;
point.mX = 1.0; point.mY = 2.0; point.mZ = 3.0;
auto [x, y, z] = point;
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Initializer Lists
Initializer lists are defined in the <initializer_list> header file and make it easy to write functions that can accept a variable number of arguments.
#include <initializer_list>
using namespace std;
int makeSum(initializer_list<int> lst)
{
int total = 0;
for (int value : lst) {
total += value;
}
return total;
}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Smart Pointers
std::unique_ptr
std::shared_ptr
auto anEmployee = make_unique<Employee>();
A unique_ptr can also be used to store a C-style array. The following example creates an array of ten Employee instances, stores it in a unique_ptr, and shows how to access an element from the array:
auto employees = make_unique<Employee[]>(10);
cout << "Salary: " << employees[0].salary << endl;
Starting with C++17, you can also store an array in a shared_ptr, whereas older versions of C++
did not allow this. Note however that make_shared<>() of C++17 cannot be used in this case. Here is an example:
shared_ptr<Employee[]> employees(new Employee[10]);
cout << "Salary: " << employees[0].salary << endl;
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
auto
using auto to deduce the type of an expression strips away reference and const qualifiers.
#include <string>
const std::string message = "Test";
const std::string& foo()
{
return message;
}
You can call foo() and store the result in a variable with the type specified as auto, as follows:
auto f1 = foo();
Because auto strips away reference and const qualifiers, f1 is of type string, and thus a copy is
made. If you want a const reference, you can explicitly make it a reference and mark it const, as follows:
const auto& f2 = foo();
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
decltype
The difference between auto and decltype is that decltype does not strip reference and const
qualifiers
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
UNIFORM INITIALIZATION
struct CircleStruct
{
int x, y;
double radius;
};
class CircleClass
{
public:
CircleClass(int x, int y, double radius)
: mX(x), mY(y), mRadius(radius) {}
private:
int mX, mY;
double mRadius;
};
CircleStruct myCircle1 = {10, 10, 2.5};
CircleClass myCircle2(10, 10, 2.5);
For the structure version, you can use the {...} syntax. However, for the class version, you need to call the constructor using function notation (...). Since C++11, you can more uniformly use the {...} syntax to initialize types, as follows:
CircleStruct myCircle3 = {10, 10, 2.5};
CircleClass myCircle4 = {10, 10, 2.5};
The definition of myCircle4 automatically calls the constructor of CircleClass. Even the use of the
equal sign is optional, so the following is identical:
CircleStruct myCircle5{10, 10, 2.5};
CircleClass myCircle6{10, 10, 2.5};
Uniform initialization is not limited to structures and classes. You can use it to initialize anything in C++. For example, the following code initializes all four variables with the value 3:
int a = 3;
int b(3);
int c = {3}; // Uniform initialization
int d{3}; // Uniform initialization
Uniform initialization can be used to perform zero-initialization* of variables; you just specify an
empty set of curly braces, as shown here:
int e{}; // Uniform initialization, e will be 0
dynamically allocated arrays
int* pArray = new int[4]{0, 1, 2, 3};
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Member Initializer list
Without initializer, two objects of Entity is made! One during the Container initialization and the other with in the constructor. This impose extra resource waste and increase the memory consumption.
class Container
{
public:
Container() { … e = Entity(); };
~Container() { cout << "Container is removed." << endl; };
private:
Entity e;
};
To avoid such cases, use initializers within the constructor.
Container():e(Entity()) { cout << "Container is made." << endl;};
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Strings
sizeof() returns the actual memory used by the string, including the ‘\0’, strlen does not count the last '\0'
char text1[] = "abcdef";
size_t s1 = sizeof(text1); // is 7
size_t s2 = strlen(text1); // is 6
However, if the C-style string is stored as a char*, then sizeof() returns the size of a pointer!
const char* text2 = "abcdef";
size_t s3 = sizeof(text2); // is platform-dependent
size_t s4 = strlen(text2); // is 6
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
C++ std::string
For compatibility, you can use the c_str() method on a string to get a const character pointer, representing a C-style string. However, the returned const pointer becomes invalid whenever the string has to perform any memory reallocation, or when the string object is destroyed. You should call the method just before using the result so that it accurately reflects the current contents of the string, and you must never return the result of c_str() called on a stack-based string object from a function.
There is also a data() method which, up until C++14, always returned a const char* just as c_str(). Starting with C++17, however, data() returns a char* when called on a non-const string.
string str0= "std::string type";
auto str1 = "const char*";
auto str2 = "std::string type"s;
cout << "Type: " << typeid(str0).name()<<endl;
cout << "Type: " << typeid(str1).name() << endl;
cout << "Type: " << typeid(str2).name() << endl;
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Numeric Conversions
to_string()
stoi()
stol()
stoul()
stoll()
stoull()
stof()
stod()
stold()
C++17
to_chars_result to_chars(char* first, char* last, IntegerT value, int base = 10);
from_chars_result from_chars(const char* first, const char* last, IntegerT& value, int base = 10);
struct to_chars_result {
char* ptr;
errc ec;
};
structured bindings
std::string out(10, ' ');
auto [ptr, ec] = std::to_chars(out.data(), out.data() + out.size(), 12345);
if (ec == std::errc()) { /* Conversion successful. */ }
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
std::string_view
string and const char* --> string_view
string str1 ------------ ----const char* str2
| |
string_view function(string_view param)
{
return …;
}
string_view -/-> string and const char*
string_view.data() --> string and const char*
string_view Literals
auto sv = "My string_view"sv;
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
TWO RULES FOR C++ DESIGN
There are two fundamental design rules in C++: abstraction and reuse
Abstraction:
use code without knowing the underlying implementation.
Your designs should take into
account existing code and reuse it
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
The fundamental tools for generic programming in C++ are templates.
The object-oriented programming paradigm adds the concept of objects, which group related data and behaviors, but it does not change the way functions and methods parameterize values.
Templates take the concept of parameterization a step further to allow you to parameterize on types as well as values.
many programmers avoid writing templates themselves. programmer needs to know at least how to use templates, because they are widely used by libraries.
Comments