We can sort the elements of the collection we have in array or vector, as well as find specific content of the element. For these purposes, we have to use the algorithm feature provided by the C++ Standard Library. Let's take a look at the following code to demonstrate the sorting element capability in the algorithm feature:
/* sort.cpp */
#include <vector>
#include <algorithm>
#include <iostream>
bool comparer(int a, int b)
{
return (a > b);
}
auto main() -> int
{
std::cout << "[sort.cpp]" << std::endl;
// Initializing a vector containing several integer elements
std::vector<int> vect = { 20, 43, 11, 78, 5, 96 };
// Displaying the original elements of the vector
std::cout << "Original Data : ";
for (auto v : vect)
std::cout << v << " ";
std::cout << std::endl;
// Sorting the vector element ascending
std::sort(std::begin(vect), std::end(vect));
// Displaying the ascending sorted elements
// of the vector
std::cout << "Ascending Sorted : ";
for (auto v : vect)
std::cout << v << " ";
std::cout << std::endl;
// Sorting the vector element descending
// using comparer
std::sort(std::begin(vect), std::end(vect), comparer);
// Displaying the descending sorted elements
// of the vector
std::cout << "Descending Sorted: ";
for (auto v : vect)
std::cout << v << " ";
std::cout << std::endl;
return 0;
}
As we see in the preceding code, we invoked the sort() method twice. First, we just supplied the range of the elements we wanted to sort. Then we added the comparison function, comparer(), to be provided to the sort() method to gain more flexibility the method has. The output we will see on the console from the preceding code is as follows:
From the preceding screenshot, we can see that we have six elements in a vector at the beginning. We then sort the elements of the vector using a simple sort() method. Then, we invoke the sort() method again, but instead of a simple sort() method, we now supply comparer() to the sort() method. As a result, the vector elements will be sorted descendingly since the comparer() function looks for the greater value from two inputs.
Now, let's move to another capability the algorithm feature has, which is finding a particular element. Let's suppose we have the Vehicle class in our code. It has two private fields named m_vehicleType and m_totalOfWheel, and we can retrieve the value from the getter methods named GetType() and GetNumOfWheel() respectively. It also has two constructors, which are the default constructor and the user-defined one. The declaration of the class should be as follows:
/* vehicle.h */
#ifndef __VEHICLE_H__
#define __VEHICLE_H__
#include <string>
class Vehicle
{
private:
std::string vehicleType;
int totalOfWheel;
public:
Vehicle(
const std::string &type,
int _wheel);
Vehicle();
~Vehicle();
std::string GetType() const {return vehicleType;}
int GetNumOfWheel() const {return totalOfWheel;}
};
#endif // End of __VEHICLE_H__
The implementation of the Vehicle class is as follows:
/* vehicle.cpp */
#include "vehicle.h"
using namespace std;
// Constructor with default value for
// m_vehicleType and m_totalOfWheel
Vehicle::Vehicle() : m_totalOfWheel(0)
{
}
// Constructor with user-defined value for
// m_vehicleType and m_totalOfWheel
Vehicle::Vehicle( const string &type, int wheel) :
m_vehicleType(type),
m_totalOfWheel(wheel)
{
}
// Destructor
Vehicle::~Vehicle()
{
}
We will store a collection of Vehicle in the vector container, and then we will search for some elements based on its property. The code will be as follows:
/* find.cpp */
#include <vector>
#include <algorithm>
#include <iostream>
#include "../vehicle/vehicle.h"
using namespace std;
bool TwoWheeled(const Vehicle &vehicle)
{
return _vehicle.GetNumOfWheel() == 2 ?
true : false;
}
auto main() -> int
{
cout << "[find.cpp]" << endl;
// Initializing several Vehicle instances
Vehicle car("car", 4);
Vehicle motorcycle("motorcycle", 2);
Vehicle bicycle("bicycle", 2);
Vehicle bus("bus", 6);
// Assigning the preceding Vehicle instances to a vector
vector<Vehicle> vehicles = { car, motorcycle, bicycle, bus };
// Displaying the elements of the vector
cout << "All vehicles:" << endl;;
for (auto v : vehicles)
std::cout << v.GetType() << endl;
cout << endl;
// Displaying the elements of the vector
// which are the two-wheeled vehicles
cout << "Two-wheeled vehicle(s):" << endl;;
auto tw = find_if(
begin(vehicles),
end(vehicles),
TwoWheeled);
while (tw != end(vehicles))
{
cout << tw->GetType() << endl ;
tw = find_if(++tw, end(vehicles), TwoWheeled);
}
cout << endl;
// Displaying the elements of the vector
// which are not the two-wheeled vehicles
cout << "Not the two-wheeled vehicle(s):" << endl;;
auto ntw = find_if_not(begin(vehicles),
end(vehicles),
TwoWheeled);
while (ntw != end(vehicles))
{
cout << ntw->GetType() << endl ;
ntw = find_if_not(++ntw, end(vehicles), TwoWheeled);
}
return 0;
}
As we can see, we instance four Vehicle objects, then store them in vector. There, we try to find the vehicle that has two wheels. The find_if() function is used for this purpose. We also have the TwoWheeled() method to provide the comparison value. Since we are finding the two-wheeled vehicle, we will inspect the totalOfWheel variable in the Vehicle class by invoking the GetNumOfWheel() method. In contrast, if we want to find the element that doesn't conform to the comparison value, we can use the find_if_not() function, which had been added in C++11. The output we get should look like this:
As we can see in the vehicle.cpp code and find.cpp code, we now add the using namespace std; line in the *.cpp files. We do this to make our coding activity become more productive since we don't have to type many words. In contrast, in vehicle.h, we still using std:: followed by the methods or properties name rather than use the std namespace at the beginning. It's best practice to not declare using namespace in header files since the header files are the files we will deliver if we create some libraries for instances. The user of our library may have another method with the same name as the function our library has. It will definitely create conflict between these two functions.
Another algorithm feature we will use most is the for_each loop. Instead of using the for loop, the use of the for_each loop will make our code more concise in many cases. It's also simpler and less error prone than a for loop because we can define a specific function for the for_each loop. Now let's refactor our previous code to use the for_each loop. The code is written as follows:
/* for_each.cpp */
#include <vector>
#include <algorithm>
#include <iostream>
#include "vehicle.h"
using namespace std;
void PrintOut(const Vehicle &vehicle)
{
cout << vehicle.GetType() << endl;
}
auto main() -> int
{
cout << "[for_each.cpp]" << endl;
// Initializing several Vehicle instances
Vehicle car("car", 4);
Vehicle motorcycle("motorcycle", 2);
Vehicle bicycle("bicycle", 2);
Vehicle bus("bus", 6);
// Assigning the preceding Vehicle instances to a vector
vector<Vehicle> vehicles = { car, motorcycle, bicycle, bus };
// Displaying the elements of the vector
cout << "All vehicles:" << endl;
for_each(begin(vehicles), end(vehicles), PrintOut);
return 0;
}
Now, with the for_each loop, we have a clearer code. We only need to provide the first and last iterator and then pass a function--the PrintOut() function in this case--that will be invoked in each element in the range.