huihut / interview

📚 C/C++ 技术面试基础知识总结,包括语言、程序库、数据结构、算法、系统、网络、链接装载库等知识及面试经验、招聘、内推等信息。This repository is a summary of the basic knowledge of recruiting job seekers and beginners in the direction of C/C++ technology, including language, program library, data structure, algorithm, system, network, link loading library, interview experience, recruitment, recommendation, etc.
https://interview.huihut.com
Other
34.8k stars 7.97k forks source link

如何定义一个只能在栈上生成对象的类 #112

Open Mq-b opened 1 year ago

Mq-b commented 1 year ago

你们描述的是:

方法:将 new 和 delete 重载为私有

事实上在我看来毫无价值,你设置私有,那我不让它优先查找内部的不就是,加有限定名字查找 :: 优先查找全局的 operator newoperator delete 不就是。

除了逆天的 msvc 有限定名字查找有问题,gccclang 都可以直接使用。code运行

#include <iostream>

struct X{
    int n{};
    X() { puts("X()"); }
    X(int v):n{v} {puts("X(int)");}
    ~X() { puts("~X()"); }
private:
    void* operator new(size_t)noexcept {return nullptr;}
    void operator delete(void*) {}
};

int main(){
    X* p = ::new X(10);
    std::cout<< p->n <<'\n';
    ::delete p;
}
frederick-vs-ja commented 1 year ago

另外,这个方法根本限制不了在创建静态或线程局部存储期的该类对象。实现中这些存储期的对象都不在栈上。

务必要注意:如果允许通过一个接口构造或按值返回该类的对象,那么用户就可以在堆上动态分配一块存储(使用 malloc::operator new),并用原位布置 new 把对象构造到堆上(如 ::new ((void*)p) T{args})。


一些近似的办法,但相当可能不符合题意:

  1. 完全限制该类的构造函数的访问,使得外部不得构造该类对象,而内部操作只在栈上构造该类对象。
  2. 生成不可移动或复制的 lambda 表达式闭包类型(需要至少 C++17)。
    
    struct Pinned {
    Pinned() = default;
    Pinned(const Pinned&) = delete;
    Pinned& operator=(const Pinned&) = delete;
    };

int main() { auto pinned_lambda = [p = Pinned{}]{}; // 此后不能在其他地方构造另一个与 pinned_lambda 拥有相同类型的对象 }



个人认为完全符合题目本意的做法基本上是不存在的,除非通过和操作系统交流,在构造函数中确定 `this` 是否指向系统划定的栈空间。
SainoNamkho commented 1 year ago
  1. 完全限制该类的构造函数的访问,使得外部不得构造该类对象,而内部操作只在栈上构造该类对象。

这种方法可能不太行

struct X {
    static X construct() { return {}; }
private:
    X() = default;
};

struct Y : X {
    Y() : X{X::construct()} {}
};

int main()
{
    X* px1 = new Y;
    X* px2 = new (operator new(sizeof(X))) X(X::construct());
}
frederick-vs-ja commented 1 year ago
  1. 完全限制该类的构造函数的访问,使得外部不得构造该类对象,而内部操作只在栈上构造该类对象。

这种方法可能不太行

struct X {
    static X construct() { return {}; }
private:
    X() = default;
};

struct Y : X {
    Y() : X{X::construct()} {}
};

int main()
{
    X* px1 = new Y;
    X* px2 = new (operator new(sizeof(X))) X(X::construct());
}

这个例子仍然相当于允许在外部构造该类对象。我指的是任何能创建该类对象的函数都不能被外部调用。