The assignment operator (=) in C++ is a fundamental tool for object-oriented programming, allowing objects to be assigned values after their creation. When working with user-defined classes, it's often necessary to provide a custom implementation of the assignment operator to ensure proper resource management and deep copying of data members. This topic explores various aspects of the assignment operator in class construction.
#include <iostream>
#include <cstring>
class String {
private:
char* data;
size_t length;
public:
String(const char* str = "") : length(strlen(str)) {
data = new char[length + 1];
strcpy(data, str);
}
~String() {
delete[] data;
}
// Copy assignment operator
String& operator=(const String& other) {
if (this != &other) {
delete[] data;
length = other.length;
data = new char[length + 1];
strcpy(data, other.data);
}
return *this;
}
void print() const {
std::cout << data << std::endl;
}
};
int main() {
String s1("Hello");
String s2;
s2 = s1; // Assignment operator called
s1.print(); // Output: Hello
s2.print(); // Output: Hello
return 0;
}
This example demonstrates a basic implementation of the assignment operator for a simple String class:
String class manages a dynamically allocated character array.operator=) is overloaded to perform a deep copy of the string data.*this) to allow chaining of assignments.#include <iostream>
#include <utility>
class Resource {
private:
int* data;
size_t size;
public:
Resource(size_t n = 0) : size(n), data(n ? new int[n]() : nullptr) {}
~Resource() {
delete[] data;
}
// Copy assignment operator
Resource& operator=(const Resource& other) {
if (this != &other) {
delete[] data;
size = other.size;
data = new int[size];
std::copy(other.data, other.data + size, data);
}
return *this;
}
// Move assignment operator
Resource& operator=(Resource&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
size = other.size;
other.data = nullptr;
other.size = 0;
}
return *this;
}
void print() const {
for (size_t i = 0; i < size; ++i) {
std::cout << data[i] << " ";
}
std::cout << std::endl;
}
};
int main() {
Resource r1(5);
Resource r2;
// Copy assignment
r2 = r1;
// Move assignment
Resource r3;
r3 = std::move(r1);
r2.print(); // Output: 0 0 0 0 0
r3.print(); // Output: 0 0 0 0 0
r1.print(); // Output: (empty, as r1 has been moved from)
return 0;
}
This example showcases both copy and move assignment operators:
Resource class manages a dynamically allocated integer array.noexcept to indicate that it doesn't throw exceptions, which can be important for optimizations and exception safety guarantees.main function, both copy and move assignments are demonstrated.#include <iostream>
#include <string>
class Animal {
protected:
std::string name;
public:
Animal(const std::string& n = "") : name(n) {}
Animal& operator=(const Animal& other) {
if (this != &other) {
name = other.name;
}
return *this;
}
virtual void speak() const {
std::cout << name << " makes a sound." << std::endl;
}
};
class Dog : public Animal {
private:
std::string breed;
public:
Dog(const std::string& n = "", const std::string& b = "") : Animal(n), breed(b) {}
Dog& operator=(const Dog& other) {
if (this != &other) {
Animal::operator=(other); // Call base class assignment operator
breed = other.breed;
}
return *this;
}
void speak() const override {
std::cout << name << " the " << breed << " barks." << std::endl;
}
};
int main() {
Dog d1("Buddy", "Labrador");
Dog d2;
d2 = d1; // Assignment operator called
d1.speak(); // Output: Buddy the Labrador barks.
d2.speak(); // Output: Buddy the Labrador barks.
return 0;
}
This example demonstrates the assignment operator in a class hierarchy:
Animal base class has a simple assignment operator that copies the name member.Dog derived class extends the assignment operator to handle its additional breed member.Animal::operator=(other) to ensure proper copying of base class members.The assignment operator is a crucial component in C++ class design, especially when dealing with resource management. Key points to remember:
*this to allow chaining of assignments.noexcept when possible for optimization and exception safety.By properly implementing assignment operators, you ensure that your classes behave correctly and efficiently when objects are assigned new values, contributing to robust and maintainable C++ code.