目的:
示例:
#include "Base.h"
#include "Derived.h"
{
static Base sb; // OK
Base ab, *hb = new Base; // OK
static Derived sd; // OK
Derived ad; // OK
Derived *pd = &ad; // OK
Derived *hd = new Derived; // Compile error, link error,
// test-unit exception, or lint gripe
struct Composite {
Base cb;
Derived cd;
} *hc = new Composite; // OK
// Edit to show side-effects of solutions which hide Base::operator new.
std::vector<Base> vb; // OK
std::vector<Derived> vd; // Error
// ...
}
如何实现Base来实现这一目标?编译时错误优先于链接时错误;但是虽然两者都优于测试单元异常,并且测试单元异常优于lint gripe,但任何不需要为每个派生更新Base.h的解决方案都可以。
编辑:出于这个问题的目的,尽管存在技术挑战,但涉及分配编译器以使其支持任意装饰的解决方案被归类为“微不足道”。
答案 0 :(得分:5)
嗯,Eclipse的答案已经消失,但我认为它是在正确的轨道上。
class Base {
public:
static Base *create() { return new Base; }
static Base *create(size_t n) { return new Base[n]; }
private:
// Prevent heap allocation
void *operator new(size_t s);
void *operator new[](size_t s);
};
这不是很好,因为它必须使用Base::create()
代替new Base
,class Derived
仍然可以实现自己的public operator new
{{1}} 1}},但我认为这是可行的。
答案 1 :(得分:4)
我在这里窃取ephemient's雷声感觉很糟糕,但他回答的唯一“错误”是他让Base的操作员成为私有的,而不是Derived's:
以下代码编译除了最后一行,我认为这是你需要的:
#include <new>
#include <vector>
class Base {};
class Derived : public Base {
private:
void * operator new (size_t);
};
void foo ()
{
static Base sb; // OK
Base ab, *hb = new Base; // OK
static Derived sd; // OK
Derived ad; // OK
Derived *pd = &ad; // OK
struct Composite {
Base cb;
Derived cd;
} *hc = new Composite; // OK
std::vector<Base> vb; // OK
std::vector<Derived> vd; // OK
Derived *hd = new Derived; // Compile error
}
<强>更新强>
正如Tal指出的那样,你仍然可以从Derived的静态成员中调用“new Derived”,但是如果没有定义“operator new”,这将导致链接错误。
但您可以稍微更改代码,以便生成编译错误(这总是更可取)。我们可以声明一个新的贴牌操作符,它仍然会阻止我们调用普通的操作符new。
class Derived : public Base {
public:
static Derived * create ()
{
return new Derived;
}
private:
class dont_dynamically_allocate_type_derived;
void * operator new (size_t, dont_dynamically_allocate_type_derived);
};
使用g ++,上面生成:
t.cc:在静态成员函数中静态Derived * Derived :: create():
t.cc:10:错误:没有匹配函数来调用Derived :: operator new(unsigned int)
t.cc:15:注意:候选人是:static void * Derived :: operator new(size_t,Derived :: dont_dynamically_allocate_type_derived)
更新(部分期货):
我想不出基类传播它本身不具有的属性的任何构造。略微调整需求,如果允许添加额外的继承层,则可以创建两个叶类,一个用于Base,用于实例化Base对象的类型,另一个用于继承:
class Base
{
public:
private:
friend class BaseInstance;
friend class BaseDerived;
~Base () {}
};
class BaseInstance : public Base
{
public:
~BaseInstance () {}
};
class BaseDerived : public Base
{
public:
~BaseDerived () {}
private:
static void * operator new (size_t);
static void * operator new[] (size_t);
};
class Derived : public BaseDerived {
public:
static Derived * create ()
{
return new Derived;
}
};
仍有一个问题,人们可以从“BaseInstance”派生出来,虽然它的名字可以明确地用来阻止这种情况发生。还有可能该类位于API的另一端,因此客户端只能看到“BaseDerived”。
答案 2 :(得分:0)
这是我在网上找到的一个解决方案:
另一个标准的新的,即所谓的“placement-new”,根本不分配内存,但可以调用它来调用任意内存块上的对象构造函数。它通常定义为:
inline void *
operator new(size_t, void *p)
{
return p;
}
答案 3 :(得分:0)
虽然有很多方法可以“阻止”代码中的对象堆分配(这里讨论并链接了几个好的),但是没有办法完全阻止它。有一些类型,例如shared_ptr&lt; T&gt;。我通常从不想要堆分配(或者我真正从不希望堆分配的变体)。对于这些,绝对最佳的解决方案是理解并记录为什么你永远不希望它们分配堆。有了这个,我们从来没有遇到过问题,因为我们对此持一致。
答案 4 :(得分:0)
如何私下派生基地?
class Base {
public:
void *operator new(size_t);
};
class Derived : private Base {
public:
};