top of page
altaybrusan

C++ Questions! (Part 2)

Updated: Aug 10, 2022



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.

1 view0 comments

Recent Posts

See All

smart pointers

The code we just described is fully functional, but, it can be strengthened, specifically with the function, AlbumDao::albums(). In this...

Comments


bottom of page