SVN / public / code / vorlesung-07 / DynamicAllocationMain.cpp

Revision 8201
Date
Committerhb1003
Download
// 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));
  }
}