This guide explains how to add a graphical desktop to an Ubuntu 24.04 VPS and access it using Windows Remote Desktop. The setup uses XFCE, xrdp, and a regular Linux user. RDP connections travel through an SSH tunnel, with xrdp listening only on localhost.
In the previous example, independent components needed shared ownership of cached objects. For a TCP session, however, we can compare two designs: multiple callbacks keeping the session alive through std::shared_ptr, or a single owning coroutine using std::unique_ptr and structured concurrency. In the second design, reading and writing still run concurrently, but the owner waits for both operations to stop before destroying the session. No shared ownership or reference counting is needed.
A practical example of using std::shared_ptr, std::weak_ptr, and a custom deleter to implement a thread-safe object cache. Multiple independent components can share the same object by retrieving it by name. When the last owner releases its shared_ptr, the custom deleter automatically removes the corresponding entry from the cache and destroys the object.
This example demonstrates how shared ownership simplifies object lifetime management without requiring centralized tracking of individual users.
C++ code below demonstrates how GCC 13 optimizes std::memcpy and a temporary std::array:
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <new>
#include <type_traits>
template <class T>
T* startLifetimeAs(void* const buffer)
{
std::array<std::byte, sizeof(T)> representation;
// Preserve the bytes before placement new ends the previous lifetime.
std::memcpy(representation.data(), buffer, representation.size());
// Start the lifetime of T in the original storage.
T* const result = ::new (buffer) T;
// Initialize the object representation of the newly created T.
std::memcpy(result, representation.data(), representation.size());
return result;
}
Below I provided an example demonstrating how placement new creates multiple objects within the same byte storage. Non-overlapping objects can coexist, while constructing an overlapping object ends the lifetime of the previously existing object. Accessing an object after its lifetime has ended causes undefined behavior.
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <new>
struct Packet final
{
std::uint64_t Data[4];
};
struct A final
{
std::uint32_t First;
std::uint32_t Second;
};
struct B final
{
std::uint32_t First;
std::uint32_t Second;
};
struct C final
{
std::uint32_t Value;
};
The first basic example illustrating this principle:
#include <cstdint>
std::uint32_t toInt(float value)
{
auto* p = reinterpret_cast<std::uint32_t*>(&value); // The cast itself is permitted.
return *p; // Undefined behavior: accesses a float object through a std::uint32_t glvalue.
}