// Copyright 2026, University of Freiburg,
// Chair of Algorithms and Data Structures
// Author: Hannah Bast <bast@cs.uni-freiburg.de>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <locale.h>
#include <utility>
#define VERBOSE true
// Class for objects that manage their own piece of memory.
class ObjectWithAllocatedMemory {
public:
// Construct with given number of bytes.
explicit ObjectWithAllocatedMemory(size_t size = 0) {
bytes_ = size == 0 ? nullptr : new char[size];
size_ = size;
id_ = nextId_++;
if (VERBOSE) {
printf("Constructor for object #%d (size = %3zu, address = %'zu)\n", id_,
size_, (size_t)bytes_);
}
}
// Copy constructor.
ObjectWithAllocatedMemory(const ObjectWithAllocatedMemory &other) {
bytes_ = new char[other.size_];
for (size_t i = 0; i < other.size_; i++) {
bytes_[i] = other.bytes_[i];
}
size_ = other.size_;
id_ = nextId_++;
if (VERBOSE) {
printf("Copy constr for object #%d (size = %3zu, address = %'zu)\n", id_,
size_, (size_t)bytes_);
}
}
// Copy assignment operator.
//
// NOTE: This does NOT create a new object, but overwrite the object.
ObjectWithAllocatedMemory &operator=(const ObjectWithAllocatedMemory &other) {
if (this != &other) {
delete[] bytes_;
bytes_ = new char[other.size_];
for (size_t i = 0; i < other.size_; i++) {
bytes_[i] = other.bytes_[i];
}
size_ = other.size_;
if (VERBOSE) {
printf("Copy assign for object #%d (size = %3zu, address = %'zu)\n",
id_, size_, (size_t)bytes_);
}
}
return *this;
}
// Move constructor.
ObjectWithAllocatedMemory(ObjectWithAllocatedMemory &&other) {
// Rob the other object of everything it has.
bytes_ = other.bytes_;
size_ = other.size_;
id_ = nextId_++;
// Reset the other object.
other.bytes_ = nullptr;
other.size_ = 0;
if (VERBOSE) {
printf("Move constr for object #%d (size = %3zu, address = %'zu)\n", id_,
size_, (size_t)bytes_);
}
}
// Move assignment operator.
//
// NOTE: This does NOT create a new object, but overwrite the object.
ObjectWithAllocatedMemory &operator=(ObjectWithAllocatedMemory &&other) {
if (this != &other) {
// Free the memory of this object.
delete[] bytes_;
// Rob the other object of everything it has.
bytes_ = other.bytes_;
size_ = other.size_;
// Reset the other object.
other.bytes_ = nullptr;
other.size_ = 0;
if (VERBOSE) {
printf("Move assign for object #%d (size = %3zu, address = %'zu)\n",
id_, size_, (size_t)bytes_);
}
}
return *this;
}
// Destructor.
~ObjectWithAllocatedMemory() {
delete[] bytes_;
if (VERBOSE) {
printf("Destructor for object #%d (size = %3zu, address = %'zu)\n", id_,
size_, (size_t)bytes_);
}
}
// Get the size.
size_t size() const { return size_; }
// Access to the i-th byte.
char &operator[](size_t i) { return bytes_[i]; }
const char &operator[](size_t i) const { return bytes_[i]; }
private:
// The bytes managed by this object.
char *bytes_;
// The number of bytes managed by this object.
size_t size_;
// The ID of this object.
int id_;
// The next ID for an object of this class.
static int nextId_;
};
// The first ID of `ObjectWithAllocatedMemory` objects is 1.
int ObjectWithAllocatedMemory::nextId_ = 1;
// Function that does something with an `ObjectWithAllocatedMemory` object.
void doSomething(const ObjectWithAllocatedMemory &object) {
printf("Size matters: %zu\n", object.size());
}
// Compute the square of a number, float or double.
float square(float x) { return x * x; }
double square(double x) { return x * x; }
// Create a random object of the given size.
ObjectWithAllocatedMemory randomObject(size_t size) {
ObjectWithAllocatedMemory result1(size);
ObjectWithAllocatedMemory result2(size);
for (size_t i = 0; i < size; ++i) {
result1[i] = drand48() * 256;
result2[i] = drand48() * 256;
}
return drand48() < 0.5 ? std::move(result1) : std::move(result2);
// return drand48() < 0.5 ? result1 : result2;
}
// Demonstrating stuff related to dynamic memory allocation for Vorlesung 6.
int main(int argc, char *argv[]) {
int demo = 6;
// Demo 6 (return value optimization).
if (demo == 6) {
ObjectWithAllocatedMemory result = randomObject(42);
}
// Demo 5 (self assignment).
if (demo == 5) {
ObjectWithAllocatedMemory o(42);
o = o;
o = std::move(o);
}
// Demo 4 (for temporary objects, move is the default).
if (demo == 4) {
ObjectWithAllocatedMemory o = ObjectWithAllocatedMemory(42);
}
// Demo 3 (usage of move).
if (demo == 3) {
ObjectWithAllocatedMemory o1(42);
ObjectWithAllocatedMemory o2(std::move(o1));
// ObjectWithAllocatedMemory o2((ObjectWithAllocatedMemory &&)o1);
}
// Demo 2 (cost of allocation).
if (demo == 2) {
if (argc != 3) {
printf("Usage: %s <n> <k>\n", argv[0]);
return 1;
}
size_t n = atoi(argv[1]);
size_t k = atoi(argv[2]);
auto start = clock();
for (size_t i = 0; i < n; ++i) {
ObjectWithAllocatedMemory tmp(k);
}
auto end = clock();
double duration = (end - start) / (double)CLOCKS_PER_SEC;
printf("Time for creating %'zu objects of size %zu: %.2f seconds\n", n, k,
duration);
}
// Demo 1 (overloading).
if (demo == 1) {
int x = 42;
printf("The square of %d is %.1f\n", x, square((float)x));
// double x = 42.0;
// printf("The square of %.1f is %.1f\n", x, square(x));
}
}