new Operator in C++
In C++, the new operator is essential for dynamic memory allocation and object construction. It allocates memory on the heap at runtime, crucial for efficient memory management and building flexible applications. When used, it initializes memory and returns a pointer to the allocated block. The operator invokes the class constructor to properly initialize objects. Understanding new is vital for managing memory in C++ and creating dynamic, runtime-adjustable applications.
Understanding Dynamic Memory Allocation
Memory resources are always premium and limited in size; thus, it becomes essential for the programmer to use memory resources in the program efficiently.
Allocating memory as and when the program is executing allows flexibility and space efficiency, primarily when the size of the variable is not known beforehand. C++ allows allocating memory either at the compile time or dynamically during the program execution to solve this problem. A new operator in C++ can dynamically allocate memory on the heap at run-time.
The figure above shows that memory allocated at run-time using the new operator is created in the heap memory segment. In contrast, variables created at compile time exist in the stack memory segment.
If sufficient memory is available, the new operator allocates the memory and returns a pointer to the first byte of the allocated memory block. When an object is created using the new keyword following things happen:
- The memory of the required size is allocated in the heap segment of the memory using the new operator.
- The class constructor is invoked to initialize the allocated memory segment properly, and the allocated memory can be stored in a pointer. This can be understood using the example mentioned below, where we are creating a Student class and creating its object using new.
Here, as soon as we create the student object, the constructor of the Student class is called.
How does new Work in C++?
new expression in C++ does three things in C++:
- Locates and reserves required storage in memory for the object to be allocated. When this step completes, the correct amount of storage is allocated for the object to use, but it is not yet an object.
- Next step is to initialize the object(s). Once this stage completes, enough information is present for allocated memory to be an object.
- Returns an address to starting byte of allocated memory that can be stored in a pointer with a pointer type derived from type-id. The program uses this returned address to access the newly allocated object in the memory.
The new operator denotes a request for memory allocation in the free space. If sufficient memory is available, the new operator initializes the memory and returns the newly allocated and initialized memory address to the pointer variable.
Syntax to Use new Operator in C++
We can allocate memory of the ClassName type using the following syntax in C++.
Here, the pointer variable is ClassNameObject, and we can use built-in data types, including arrays or user-defined data types like structure and class. For example,
We can also use the new operator to initialize value or create a memory block, as shown below.
From the image, you can see how ten continuous memory blocks are initialized. The address of the first block is stored in the pointer *p. new operator also supports different parameters covered in the next section.
Let us detail the syntax of the new operator in C++ with all its elements.
Here, new-placement and new-initializer are optional elements. We will understand the function of different elements in the upcoming sections of the article.
A new operator defined in the <new> header in C++ can have four different arguments that are:
- count: This parameter defines the number of bytes to allocate in memory.
- ptr: This tells the returned pointer type, which points to the starting byte of the allocated memory block.
- tag (optional): This parameter in the expression is a disambiguation tag used to select non-throwing overloads.
- al (optional): This is used to tell alignment to use. This behavior is undefined if the argument passed is not a valid alignment value.
Allocated Storage Space
There are three ways to allocate storage space using the new operator in C++. Let us understand each of them:
1. Throwing Allocation
In this allocation, memory is allocated of size defined by the user, and a non-null pointer to the first byte of the block is returned. It is suitably aligned to represent any object of the mentioned size. If the memory allocation fails, the program throws a badalloc exception.
For example: the below image shows the program terminates with a badalloc exception because we are trying to create an array of size , beyond the allowed capacity.
2. Nothrow Allocation
This allocation method works the same way as the previous method (throwing allocation), except that when an error occurs during allocation and allocation fails, it returns a null pointer instead of throwing an exception.
This method returns a pointer, and no storage is allocated. Still, if a new expression calls the function, the proper initialization of the object will be performed for class objects (that includes a call to the class's default constructor).
The Grammar Elements of new
Let us understand different grammar elements of the new operator, which we mentioned in the syntax of new:
new-placement (optional): It provides a way of passing extra arguments if we want to overload new. This element allows us to allocate memory at a known memory address. This can be observed in the example below, where we allocate the memory in a known location.
Explanation: In this example, the new value of var is assigned at the memory address of var because of new-placement used with new. This is clear because the memory address of both &var and mem is equal.
type-id: Type-id specifies the data type to be allocated, it can either be a built-in data type or can be a user-defined data type, and if the type specification is complex, it can be enclosed in parenthesis to force the order of binding. Auto keyword can be used to allow the compiler to determine the type-id.
new-initializer (optional): This element is used to assign value to the newly allocated object, which cannot be specified for the arrays. The new operator can only create an array of objects when the class has a default constructor.
noptr-new-declarator: This part specifies the bounds of an array. When a multidimensional array is allocated using new, all the dimensions except the first must be a constant expression that evaluates to a positive value convertible to std::size_t.
Initializing Objects Allocated with new
In C++, an optional new field, new-initialized, is included in the grammar for the new operator that allows new objects to be initialized with the user-defined constructor.
Let us see an example to understand how initialization expression is used with the new operator. In the example, we are creating a new class, Account, and using the new keyword to create its object.
In this example, when the savingsAccount object is allocated using the new operator, no default initialization is specified, so the default constructor is called Account(), but for currentAccount, it is explicitly initialized with a value 34.98 because the second constructor with an argument is called in this case Account(double).
If an object is of a class type and has constructors, the object can be initialized with a new operator if one of the following conditions are met:
- The arguments in the new operator match the arguments in the constructor.
- The class has a default constructor called without any argument.
Note: If the memory allocation fails, the new operator returns a value of 0, and no initialization is done. Also, if the memory allocation fails, some expressions following it may not be evaluated thoroughly.
Unique Properties of the Default Allocation and Deallocation Functions
The default allocation and deallocation functions are special components of the standard library and have the following unique properties.
Global: All three versions of the new operator are declared in the global namespace (namespaces allow the group of the named entities that otherwise would have global scope, giving them namespace scope) and not within the standard std namespace.
Implicit: The two allocating versions that throw an exception and nothrow expression are implicitly declared in all the translation units of a C++ program, even if the header <new> is included or not.
Replaceable: A program can provide its definition that can replace the default defined new operator for the two allocating versions throwing an exception and nothrow expression. We can overload it for a specific type.
Let us see an example where we use the new operator to allocate memory for both predefined and custom data types and the new operator to create an array of numbers.
Here, we are creating three different types of variables where number is an integer type variable, numArray is an integer array that can store ten values, and student is a variable of data type Student, and all three are dynamically created using new operator. At the end of the program, we deallocate the memory block using the delete keyword.
new operator is used to dynamically allocate memory blocks in the heap segment of the memory in C++. The compiler allocates this memory during the time the program executes.
The class constructor is invoked to initialize the allocated memory segment properly, and the new expression returns the pointer pointing to the first memory block of the allocated segment.
new operator has four grammar elements: new-placement, type-id, new-initializer, and noptr-new-declarator.
If the memory allocation fails, the new operator returns a value of , and no initialization is done. Also, if the memory allocation fails, some expressions following it may not be evaluated thoroughly.