tl;博士;如何使对象的每个实例都具有相同的常量地址,然后在用户想要更改它后对其进行分配?
我需要开发自己的 NULL (ish) 对象,并且希望默认初始化为 NullFoo (在值和地址中),因为每次构造对象时它只会生成 NullFoo 但在不同的地址上。
在上下文中,空(ish)值并不重要,它形成序列化的信息管理(即它未序列化)。让每个“未填充”数据结构从某处开始而不浪费空间,在动态内存管理中仍然非常有用。
所以请考虑下面的示例代码:
//pretty lights
#define CLOG(x) std::clog << "\033[48;2;100;150;100m" << "created\t" << x << "\033[m\n";
#define cLOG(x) std::clog << "\033[48;2;100;150;100m" << "copied\t" << x << "\033[m\n";
#define rLOG(x) std::clog << "\033[48;2;100;150;150m" << "reseted\t" << x << "\033[m\n";
#define dLOG(x) std::clog << "\033[48;2;150;100;100m" << "deleted\t" << x << "\033[m\n";
#define aLOG(x) std::clog << "\033[48;2;100;130;100m" << "assigned\t" << x << "\033[m\n";
#define caLOG(x) std::clog << "\033[48;2;100;130;120m" << "copy assigned\t" << x << "\033[m\n";
struct foo{
int objdata;
foo()
{
this->objdata = 0;
CLOG(*this);
}
foo(const foo &other)
{
this->objdata = other.objdata;
cLOG(*this);
}
foo(foo &&other)
{
this->objdata = other.objdata;
cLOG(*this);
other.objdata =0;
rLOG(other);
}
~foo()
{
dLOG(*this)
};
foo &operator=(int value)
{
this->objdata = value;
aLOG(*this);
return *this;
}
foo &operator=(const foo &other)
{
this->objdata =other.objdata;
caLOG(*this);
return *this;
}
foo &operator=(foo &&other)
{
this->objdata = other.objdata;
caLOG(*this);
other.objdata = 0;
rLOG(other);
return *this;
}
//required for pre-processor shenanigans (just visual que)
friend std::ostream &operator<<(std::ostream &, const foo &);
};
const foo NullFoo;
std::ostream &operator<<(std::ostream &os, const foo &f){
os << f.objdata<< '\t' << &f << '\t' << &NullFoo;
return os;
}
和入口点:
int main(){
{ // scope so pretty lights show
std::cout << NullFoo << std::endl;
foo a;
std::cout << a << std::endl;
a = rand()%0x100;
std::cout << a << std::endl;
}
return 1;
}
输出:
created 0 0x5600e97b13ec 0x5600e97b13ec
0 0x5600e97b13ec 0x5600e97b13ec
created 0 0x7fff9ae33454 0x5600e97b13ec
0 0x7fff9ae33454 0x5600e97b13ec
assigned 103 0x7fff9ae33454 0x5600e97b13ec
103 0x7fff9ae33454 0x5600e97b13ec
deleted 103 0x7fff9ae33454 0x5600e97b13ec
deleted 0 0x5600e97b13ec 0x5600e97b13ec
如您所见,eac 初始化在新地址上,随后被销毁。
NullFoo
在 0x5600e97b13ec
上创建,每个后续构造函数都会创建一个新地址(在本例中为 0x7fff9ae33454
)。似乎内存是在调用构造函数之前分配的(但我不知道如何检查)。
如何使对象的每个实例都具有相同的常量地址,然后在用户想要更改它后分配它?
正在开发
Ubuntu 22.04
LTS、g++
11.4.0.
两个对象不能共享相同的地址。并且一个对象不能改变地址。
要从相同的对象开始并通过突变进行更改......这就是引用语义,并且您可以使用评论中描述的(原始或智能)指针类型来获得它。
例如,
const foo NullFoo;
// becomes -->
const std::shared_ptr<const foo> NullFoo;
// or
const foo NullFooObj;
const foo* const NullFoo = &NullFooObj;
foo a;
// becomes -->
std::shared_ptr<const foo> a = NullFoo;
// or
const foo* a = NullFoo;
a = rand()%0x100;
// becomes -->
a = std::make_shared<const foo>(rand()%0x100);
// or
foo aobj = rand()%0x100;
a = &aobj;
可以将其封装在包装类型中。使用原始指针需要一个具有兼容生命周期的对象,而shared_ptr不需要,但需要堆。原始指针通常太不灵活,无法用于通用 API,因为调用者必须确保生命周期,这意味着所有工作必须一次性完成(无队列、异步)。因此,在地址(对象标识)很重要的情况下,您可能会看到共享指针和所需的堆分配。
在上下文中,空(ish)值并不重要,它形成序列化的信息管理(即它未序列化)。让每个“未填充”数据结构从某处开始而不浪费空间,在动态内存管理中仍然非常有用。
您所要求的东西不适合此用例。在问题的代码中,没有发生内存分配或堆使用。每个 foo 要么是静态对象,要么在堆栈上。尝试通过特定地址识别空对象会迫使内存管理策略远远不太理想。
解决此问题的更有效方法是使用可以表示无效性的类型:
foo a;
// becomes -->
std::optional<foo> a;
// this is unchanged
a = rand()%0x100;
确实,从技术上讲,堆栈上有用于保存 foo 的空间。然而,分配比堆分配便宜几个数量级。
或者,可以根据某些类型属性决定不进行序列化:例如不要序列化等于相同类型的值初始化对象的值,或者不要序列化等于特征类型指定的值的值。