简体   繁体   中英

C++ class that can be put *only* on the stack?

How can one create a C++ class whose objects can be put only on the stack, as single instances, but

  • not into a std::vector (or other std container) and also not in a plain array no matter where these are allocated.
  • not on the heap (answer linked to)
  • not as member into another object, if that object itself lives in a container or in the free store.

Note: How do I prevent a class from being allocated via the 'new' operator? (I'd like to ensure my RAII class is always allocated on the stack.) asks how to avoid the free store, but that's only half the answer. I guess(?) for containers, one can do something similar with the placement-new operator, but what's with arrays and the data-member case?

The restriction should be compiler-enforced. (No runtime checking.)

By example:

class User {
  ...
  StackOnly m; // may be OK (depending if below implementable)
};

void f() {
  StackOnly obj; //ok
  StackOnly arrobj[42]; // fail!
  StackOnly* pObj = new StackOnly(); // fail!
  std::vector<StackOnly> v; // fail!

  User obj; //ok
  User arrobj[42]; // fail!
  User* pObj = new StackOnly(); // fail!
  std::vector<User> v; // fail!
}

The following class allows only: X&& x = X::MakeInstance(); or const X& x = X::MakeInstance(); usages:

class X
{
public:
    X(const X&) = delete;
    X(X&&) = delete;
    X& operator =(const X&) = delete;
    X& operator =(X&&) = delete;

public:
    static X MakeInstance() { return {}; }

    static void* operator new     (size_t) = delete;
    static void* operator new[]   (size_t) = delete;
    static void  operator delete  (void*)  = delete;
    static void  operator delete[](void*)  = delete;

private:
    X() = default;
};

What we can do is some nonportable hack whereby we obtain the stack pointer like this:

volatile char var, *approximate_stack_ptr = &var;

We do this inside the constructor for the object.

Then we can check that the address of the object itself (the this pointer) is within, say, 4096 bytes of approximate_stack_pointer ; ie

// formally undefined behavior here, but chances are good
// it will work with many compilers:

ptrdiff_t delta = (char *) this - approximate_stack_ptr;

// Then, check that delta is between -4096 and 4096

The 4096 value is generous because it can be; the actual delta will likely be a lot smaller. There is no need to make it strict, though. An object that is not defined on the stack in any valid way will be significantly far away from the stack pointer; much farther than 4096 bytes. (Unless we are dealing with a very tiny embedded system implementation where 4096 is like half the addressable space.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM