Assignment Operator Overload
Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 44 of 104.
The copy assignment operator (=) is called when you assign one existing object to another. Unlike the copy constructor (which creates a new object), the assignment operator works with an object that already exists and may have resources that need to be cleaned up first.
class Buffer {
int* data;
size_t size;
public:
Buffer(size_t s) : size(s), data(new int[s]) {}
~Buffer() { delete[] data; }
Buffer& operator=(const Buffer& other) {
if (this != &other) { // 1. Self-assignment check
delete[] data; // 2. Clean up existing resource
size = other.size; // 3. Copy the data
data = new int[size];
for (size_t i = 0; i < size; ++i)
data[i] = other.data[i];
}
return *this; // 4. Return *this
}
};The self-assignment check (this != &other) is critical. Without it, b = b; would delete the data before trying to copy it, causing undefined behavior.
The operator returns *this by reference to enable chaining like a = b = c;. This matches how built-in types behave.
For classes managing resources, you'll often implement both copy and move assignment operators:
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
delete[] data; // Clean up current resource
data = other.data; // Steal the resource
size = other.size;
other.data = nullptr; // Leave source in valid state
other.size = 0;
}
return *this;
}The move assignment operator transfers ownership instead of copying, making operations like vec[0] = createBuffer(); much more efficient.
Challenge
EasyLet's build a DynamicString class that manages a dynamically allocated character array and properly handles assignment between existing objects. This is a practical scenario where you need to safely transfer string data from one object to another without memory leaks or dangling pointers.
You'll create two files to organize your code:
DynamicString.h: Define aDynamicStringclass that stores a C-style string using dynamic memory. Your class should manage:- A private
char*pointer for the string data - A private
size_tfor the length - A constructor that takes a
const char*and creates a deep copy - A destructor that properly frees the allocated memory
- A copy assignment operator that performs a deep copy with self-assignment protection
- A move assignment operator that transfers ownership efficiently
- A
c_str()method that returns the internal string (const) - A
length()method that returns the string length (const)
Your copy assignment operator must follow the four essential steps: check for self-assignment, clean up existing resources, copy the new data, and return
*this. The move assignment operator should steal the source's pointer and leave the source in a valid empty state (nullptr with length 0).- A private
main.cpp: Read three strings from input (each on a separate line). Create threeDynamicStringobjects:str1with the first input,str2with the second input, andstr3with the third input.Demonstrate your assignment operators:
- Print the initial state of all three strings
- Use copy assignment:
str1 = str2; - Print str1 and str2 after copy assignment (both should have the same content)
- Use move assignment:
str2 = std::move(str3); - Print str2 after move assignment, and str3's length (should be 0 after being moved from)
Output format:
Initial: str1: <value> str2: <value> str3: <value> After copy (str1 = str2): str1: <value> str2: <value> After move (str2 = std::move(str3)): str2: <value> str3 length: 0
Use <cstring> for strlen and strcpy. Mark your move assignment operator as noexcept. Include <utility> in main.cpp for std::move. Don't forget header guards in your header file.
Cheat sheet
The copy assignment operator (=) assigns one existing object to another. It must handle cleanup of existing resources before copying new data.
Copy Assignment Operator Structure
ClassName& operator=(const ClassName& other) {
if (this != &other) { // 1. Self-assignment check
// 2. Clean up existing resources
// 3. Copy data from other
}
return *this; // 4. Return *this
}Example with Dynamic Memory
class Buffer {
int* data;
size_t size;
public:
Buffer& operator=(const Buffer& other) {
if (this != &other) {
delete[] data; // Clean up existing resource
size = other.size;
data = new int[size];
for (size_t i = 0; i < size; ++i)
data[i] = other.data[i];
}
return *this;
}
};The self-assignment check (this != &other) prevents deleting data before copying it in cases like b = b;.
Returning *this by reference enables chaining: a = b = c;.
Move Assignment Operator
The move assignment operator transfers ownership instead of copying, improving efficiency:
ClassName& operator=(ClassName&& other) noexcept {
if (this != &other) {
// Clean up current resource
// Steal the resource from other
// Leave other in valid state (nullptr, 0, etc.)
}
return *this;
}Example Move Assignment
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data; // Steal the resource
size = other.size;
other.data = nullptr; // Leave source valid
other.size = 0;
}
return *this;
}Mark move assignment as noexcept for optimal performance with standard containers.
Try it yourself
#include <iostream>
#include <string>
#include <utility>
#include "DynamicString.h"
using namespace std;
int main() {
// Read three strings from input
string input1, input2, input3;
getline(cin, input1);
getline(cin, input2);
getline(cin, input3);
// Create three DynamicString objects
DynamicString str1(input1.c_str());
DynamicString str2(input2.c_str());
DynamicString str3(input3.c_str());
// TODO: Print initial state of all three strings
// Format:
// Initial:
// str1: <value>
// str2: <value>
// str3: <value>
// TODO: Use copy assignment: str1 = str2;
// TODO: Print str1 and str2 after copy assignment
// Format:
// After copy (str1 = str2):
// str1: <value>
// str2: <value>
// TODO: Use move assignment: str2 = std::move(str3);
// TODO: Print str2 after move, and str3's length
// Format:
// After move (str2 = std::move(str3)):
// str2: <value>
// str3 length: 0
return 0;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container