Prefer Initialization Over Assignment Of Rents

Copy constructors sounds like a topic for an article from 1989. And yet, the changes in the new C++ standard affect the design of a class’ special member functions fundamentally. Find out more about the impact of move semantics on objects’ behavior and learn how to implement the move constructor and the move assignment operator in C++11.

C++11 is the informal name for ISO/IEC 14882:2011, the new C++ standard that was published in September 2011. It includes the TR1 libraries and a large number of new core features (a detailed discussion about these new C++11 features is available here; also see The Biggest Changes in C++11 (and Why You Should Care)):

  • Initializer lists
  • Uniform initialization notation
  • Lambda functions and expressions
  • Strongly-typed enumerations
  • Automatic type deduction in declarations
  • storage class
  • Control and query of object alignment
  • Static assertions
  • Type
  • Variadic templates

Important as these features may be, the defining feature of C++11 is rvalue references.

The Right Time for Rvalue References

Rvalue references are a new category of reference variables that can bind to rvalues.  Rvalues are slippery entities, such as temporaries and literal values; up until now, you haven’t been able to bind these safely to reference variables.

Technically, an rvalue is an unnamed value that exists only during the evaluation of an expression. For example, the following expression produces an rvalue:

x+(y*z); // A C++ expression that produces a temporary 

C++ creates a temporary (an rvalue) that stores the result of , and then adds it to . Conceptually, this rvalue evaporates by the time you reach the semicolon at the end of the full expression.

A declaration of an rvalue reference looks like this:

std::string&& rrstr; //C++11 rvalue reference variable

The traditional reference variables of C++ e.g.,

std::string& ref;

are now called lvalue references.

Rvalue references occur almost anywhere, even if you don’t use them directly in your code. They affect the semantics and lifetime of objects in C++11. To see how exactly, it’s time to talk about move semantics.

Get to Know Move Semantics

Hitherto, copying has been the only means for transferring a state from one object to another (an object’s state is the collective set of its non-static data members’ values). Formally, copying causes a target object to end up with the same state as the source , without modifying . For example, when you copy a string to , the result is two identical strings with the same state as .

And yet, in many real-world scenarios, you don’t copy objects but move them. When my landlord cashes my rent check, he moves money from my account into his. Similarly, removing the SIM card from your mobile phone and installing it in another mobile is a move operation, and so are cutting-and-pasting icons on your desktop, or borrowing a book from a library.

Notwithstanding the conceptual difference between copying and moving, there’s a practical difference too: Move operations tend to be faster than copying because they transfer an existing resource to a new destination, whereas copying requires the creation of a new resource from scratch. The efficiency of moving can be witnessed among the rest in functions that return objects by value. Consider:

string func()
{
string s;
//do something with s
return s;
}
string mystr=func();

When returns, C++ constructs a temporary copy of on the caller’s stack memory. Next, is destroyed and the temporary is used for copy-constructing . After that, the temporary itself is destroyed. Moving achieves the same effect without so many copies and destructor calls along the way.

Moving a string is almost free; it merely assigns the values of the source’s data members to the corresponding data members of the target. In contrast, copying a string requires the allocation of dynamic memory and copying the characters from the source.

Move Special Member Functions

C++11 introduces two new special member functions: the move constructor and the move assignment operator. They are an addition to the fabulous four you know so well:

  • Default constructor
  • Copy constructor
  • Copy assignment operator
  • Destructor

If a class doesn’t have any user-declared special member functions (save a default constructor), C++ declares its remaining five (or six) special member functions implicitly, including a move constructor and a move assignment operator. For example, the following class

class S{};

doesn’t have any user-declared special member functions. Therefore, C++ declares all of its six special member functions implicitly. Under certain conditions, implicitly declared special member functions become implicitly defined as well. The implicitly-defined move special member functions move their sub-objects and data members in a member-wise fashion. Thus, a move constructor invokes its sub-objects’ move constructors, recursively. Similarly, a move assignment operator invokes its sub-objects’ move assignment operators, recursively.

What happens to a moved-from object? The state of a moved-from object is unspecified. Therefore, always assume that a moved-from object no longer owns any resources, and that its state is similar to that of an empty (as if default-constructed) object. For example, if you move a string to , after the move operation the state of is identical to that of before the move, whereas is now an empty (though valid) string object.

Designing a Move Constructor

A move constructor looks like this:

C::C(C&& other); //C++11 move constructor

It doesn’t allocate new resources. Instead, it pilfers‘s resources and then sets to its default-constructed state.

Let’s look at a concrete example. Suppose you’re designing a class that represents a memory buffer:

class MemoryPage
{
size_t size;
char * buf;
public:
explicit MemoryPage(int sz=512):
size(sz), buf(new char [size]) {}
~MemoryPage( delete[] buf;}
//typical C++03 copy ctor and assignment operator
MemoryPage(const MemoryPage&);
MemoryPage& operator=(const MemoryPage&);
};

A typical move constructor definition would look like this:

//C++11
MemoryPage(MemoryPage&& other): size(0), buf(nullptr)
{
// pilfer other’s resource
size=other.size;
buf=other.buf;
// reset other
other.size=0;
other.buf=nullptr;
}

The move constructor is much faster than a copy constructor because it doesn’t allocate memory nor does it copy memory buffers.

Designing a Move Assignment Operator

A move assignment operator has the following signature:

C& C::operator=(C&& other);//C++11 move assignment operator

A move assignment operator is similar to a copy constructor except that before pilfering the source object, it releases any resources that its object may own. The move assignment operator performs four logical steps:

  • Release any resources that currently owns.
  • Pilfer ‘s resource.
  • Set to a default state.
  • Return .

Here’s a definition of ‘s move assignment operator:

//C++11
MemoryPage& MemoryPage::operator=(MemoryPage&& other)
{
if (this!=&other)
{
// release the current object’s resources
delete[] buf;
size=0;
// pilfer other’s resource
size=other.size;
buf=other.buf;
// reset other
other.size=0;
other.buf=nullptr;
}
return *this;
}

Overloading Functions

The overload resolution rules of C++11 were modified to support rvalue references. Standard Library functions such as now define two overloaded versions: one that takes for lvalue arguments as before, and a new one that takes a parameter of type for rvalue arguments. The following program populates a vector with objects using two () calls:

#include <vector>
using namespace std;
int main()
{
vector<MemoryPage> vm;
vm.push_back(MemoryPage(1024));
vm.push_back(MemoryPage(2048));
}

Both calls resolve as because their arguments are rvalues. moves the resources from the argument into ‘s internal objects using ‘s move constructor. In older versions of C++, the same program would generate copies of the argument since the copy constructor of would be called instead.

As I said earlier, is called when the argument is an lvalue:

#include <vector>
using namespace std;
int main()
{
vector<MemoryPage> vm;
MemoryPage mp1(1024);//lvalue
vm.push_back(mp); //push_back(const T&)
}

However, you can enforce the selection of even in this case by casting an lvalue to an rvalue reference using :

//calls push_back(T&&)

vm.push_back(static_cast<MemoryPage&&>(mp));

Alternatively, use the new standard function for the same purpose:

vm.push_back(std::move(mp));//calls push_back(T&&)

It may seem as if is always the best choice because it eliminates unnecessary copies. However, remember that empties its argument. If you want the argument to retain its state after a call, stick to copy semantics. Generally speaking, don’t rush to throw away the copy constructor and the copy assignment operator. In some cases, the same class could be used in a context that requires pure copy semantics, whereas in other contexts move semantics would be preferable.

In Conclusion

C++11 is a different and better C++. Its rvalue references and move-oriented Standard Library eliminate many unnecessary copy operations, thereby improving performance significantly, with minimal, if any, code changes. The move constructor and the move assignment operator are the vehicles of move operations. It takes a while to internalize the principles of move semantics – and to design classes accordingly. However, the benefits are substantial. I would dare predicting that other programming languages will soon find ways to usher-in move semantics too.

Danny Kalev is a certified system analyst by the Israeli Chamber of System Analysts and software engineer specializing in C++. Kalev has written several C++ textbooks and contributes C++ content regularly on various software developers’ sites. He was a member of the C++ standards committee and has a master’s degree in general linguistics.

See also:

 











Some operating systems or software package vendors may provide ready-to-use, pre-built software packages for Kea. Installing a pre-built package means you do not need to install the software required only to build Kea and do not need to make the software.

FreeBSD ports, NetBSD pkgsrc, and Debian testing package collections provide all the prerequisite packages.

The following is the directory layout of the complete Kea installation. (All directory paths are relative to the installation directory):

  • — utility programs.
  • — configuration files.
  • — C++ development header files.
  • — libraries.
  • — server software and commands used by the system administrator.
  • — configuration specifications and examples.
  • — this guide, other supplementary documentation, and examples.
  • — manual pages (online documentation).
  • — server identification, lease databases, and log files.

3.2. Installation Hierarchy

In addition to the run-time requirements (listed in Section 1.2, “Required Software at Run-time”), building Kea from source code requires various development include headers and program development tools.

Note

Some operating systems have split their distribution packages into a run-time and a development package. You will need to install the development package versions, which include header files and libraries, to build Kea from the source code.

Building from source code requires the following software installed on the system:

  • Boost C++ Libraries (http://www.boost.org/). The oldest Boost version used for testing is 1.57 (it may work with older versions). Boost system library is required. Building boost header only is no longer recommended.

  • Botan (at least version 1.8) or OpenSSL (at least version 1.0.1).

  • log4cplus (at least version 1.0.3) development include headers.

  • A C++ compiler (with C++11 support) and standard development headers. Kea builds have been tested with GCC g++ 4.7.2 4.7.3 4.8.2 4.8.4 4.8.5 4.9.3 4.9.4 5.3.1 5.4.0 6.3.0 6.3.1 clang-800.0.38 clang-802.0.42 clang-900.0.37

  • The development tools automake, libtool, pkg-config.

  • The MySQL client and the client development libraries, when using the --with-dhcp-mysql configuration flag to build the Kea MySQL database backend. In this case an instance of the MySQL server running locally or on a machine reachable over a network is required. Note that running the unit tests requires a local MySQL server.

  • The PostgreSQL client and the client development libraries, when using the --with-dhcp-pgsql configuration flag to build the Kea PostgreSQL database backend. In this case an instance of the PostgreSQL server running locally or on some other machine, reachable over the network from the machine running Kea, is required. Note that running the unit tests requires a local PostgreSQL server.

  • googletest (version 1.8 or later), when using the --with-gtest configuration option to build the unit tests.

  • The documentation generation tools elinks, docbook-xsl, libxslt and Doxygen, if using the --enable-generate-docs configuration option to create the documentation.

3.3. Building Requirements

Kea is open source software written in C++. It is freely available in source code form from ISC as a downloadable tar file. A copy of the Kea source code repository is accessible from Github (https://github.com/isc-projects/kea). Kea may also be available in pre-compiled ready-to-use packages from operating system vendors.

Downloading this "bleeding edge" code is recommended only for developers or advanced users. Using development code in a production environment is not recommended.

Note

When building from source code retrieved via Git, additional software will be required: automake (v1.11 or later), libtoolize, and autoconf (v2.69 or later). These may need to be installed.

The latest development code is available on Github (see https://github.com/isc-projects/kea). The Kea source is public and development is done in the “master” branch.

The code can be checked out from :

$

The code checked out from the git repository does not include the generated configure script, Makefile.in files, nor their related build files. They can be created by running autoreconf with the switch. This will run autoconf, aclocal, libtoolize, autoheader, automake, and related commands.

Write access to the Kea repository is only granted to ISC staff. If you are a developer planning to contribute to Kea, please fork our Github repository and use the "pull request" mechanism to request integration of your code. Please consult https://help.github.com/articles/fork-a-repo/ for help on how to fork a Github repository. The Kea Developer's Guide contains more information about the process, as well as describing the requirements for contributed code to be accepted by ISC.

Kea uses the GNU Build System to discover build environment details. To generate the makefiles using the defaults, simply run:

$

Run ./configure with the switch to view the different options. Some commonly-used options are:

--prefix
Define the installation location (the default is ).
--with-boost-include
Define the path to find the Boost headers.
--with-botan-config
Specify the path to the botan-config script to build with Botan for cryptographic functions.
--with-dhcp-mysql
Build Kea with code to allow it to store leases (and access host reservations) in a MySQL database.
--with-dhcp-pgsql
Build Kea with code to allow it to store leases (and access host reservations) in a PostgreSQL database.
--with-gtest-source
Enable the building of the C++ Unit Tests using the Google Test framework. This option specifies the path to the gtest source. (If the framework is not installed on your system, it can be downloaded from https://code.google.com/p/googletest.)
--with-log4cplus
Define the path to find the Log4cplus headers and libraries.
--with-openssl
Replace Botan by the OpenSSL the cryptographic library. By default configure searches for a valid Botan installation: if one is not found, it searches for OpenSSL.

For example, the following command configures Kea to find the Boost headers in /usr/pkg/include, specifies that PostgreSQL support should be enabled, and sets the installation location to /opt/kea:

$

If you have some problems with building Kea using the header-only Boost code or you'd like to use the Boost system library (assumed for the sake of this example to be located in /usr/pkg/lib):

$

If configure fails, it may be due to missing or old dependencies.

If configure succeeds, it displays a report with the parameters used to build the code. This report is saved into the file and is also embedded into the executable binaries, e.g., .

3.4.3. Configure Before the Build

After the configure step is complete, build the executables from the C++ code and prepare the Python scripts by running the command:

$

To install the Kea executables, support files, and documentation, issue the command:

$

Do not use any form of parallel or job server options (such as GNU make's -j option) when performing this step: doing so may cause errors.

Note

The install step may require superuser privileges.

If required, run ldconfig as root with (or with /lib if configured with --prefix) in (or the relevant linker cache configuration file for your OS):

$

Note

If you do not run ldconfig where it is required, you may see errors like the following:

program: error while loading shared libraries: libkea-something.so.1: cannot open shared object file: No such file or directory

3.4. Installation from Source

Kea 0.9 introduced configuration backends that are switchable during the compilation phase. Only one backend, JSON, is currently supported.

JSON
JSON is the default configuration backend that allows Kea to read JSON configuration files from disk. It does not require any framework and thus is considered more lightweight. It allows dynamic on-line reconfiguration using Kea API.

3.5. Selecting the Configuration Backend

Kea stores its leases in a lease database. The software has been written in a way that makes it possible to choose which database product should be used to store the lease information. At present, Kea supports four database backends: MySQL, PostgreSQL, Cassandra and Memfile. To limit external dependencies, MySQL, PostgreSQL and Cassandra support are disabled by default and only Memfile is available. Support for the optional external database backend must be explicitly included when Kea is built. This section covers the building of Kea with one of the optional backends and the creation of the lease database.

Note

When unit tests are built with Kea (the --with-gtest configuration option is specified), the databases must be manually pre-configured for the unit tests to run. The details of this configuration can be found in the Kea Developer's Guide.

Install MySQL according to the instructions for your system. The client development libraries must be installed.

Build and install Kea as described in Chapter 3, Installation, with the following modification. To enable the MySQL database code, at the "configure" step (see Section 3.4.3, “Configure Before the Build”), the --with-dhcp-mysql switch should be specified:

If MySQL was not installed in the default location, the location of the MySQL configuration program "mysql_config" should be included with the switch, i.e.

See Section 4.3.2.1, “First Time Creation of the MySQL Database” for details regarding MySQL database configuration.

3.6.1. Building with MySQL Support

Install PostgreSQL according to the instructions for your system. The client development libraries must be installed. Client development libraries are often packaged as "libpq".

Build and install Kea as described in Chapter 3, Installation, with the following modification. To enable the PostgreSQL database code, at the "configure" step (see Section 3.4.3, “Configure Before the Build”), the --with-dhcp-pgsql switch should be specified:

If PostgreSQL was not installed in the default location, the location of the PostgreSQL configuration program "pg_config" should be included with the switch, i.e.

See Section 4.3.3.1, “First Time Creation of the PostgreSQL Database” for details regarding PostgreSQL database configuration.

3.6.2. Building with PostgreSQL support

Install Cassandra according to the instructions for your system. The Cassandra project website contains useful pointers: http://cassandra.apache.org.

Download and compile cpp-driver from DataStax. For details regarding dependencies for building cpp-driver, see the project homepage https://github.com/datastax/cpp-driver. In June 2016, the following commands were used:

$ $ $ $ $

As of June 2016, cpp-driver does not include cql_config script yet. Work is in progress to contribute such a script to the cpp-driver project but, until that is complete, intermediate steps that need to be conducted. A cql_config script is present in the tools/ directory of the Kea sources. Before using it, please edit cql_config_defines.sh in the same directory and change the environment variable CPP_DRIVER_PATH to point to the directory, where cpp-driver sources are located. (If the cpp-driver sources already provide cql_config script please use that rather than the version from Kea sources.)

Build and install Kea as described in Chapter 3, Installation, with the following modification. To enable the Cassandra (CQL) database code, at the "configure" step (see Section 3.4.3, “Configure Before the Build”), do:

3.6.3. Building with CQL (Cassandra) support

3.6. DHCP Database Installation and Configuration

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *