当访问联合的另一个成员时,C++ 标准过去对发生的情况保持沉默,但这一点已被修复,以解释允许对联合对象的成员访问是为了分配给该尚不存在的对象,这会神奇地通过分配给对象或其成员之一来创建对象。本质上,成员访问运算符返回未来对象的承诺,您必须将其与赋值一起使用。
给定
union U {
int i;
long l;
};
Run Code Online (Sandbox Code Playgroud)
我们可以通过赋值创建一个i或成员子对象:l
U u;
u.i = 2; // creates an int
u.l = 3; // creates a long (destroys the int)
Run Code Online (Sandbox Code Playgroud)
但是当联合对象被复制时会发生什么?编译器如何“知道”要“创建”哪个成员?
U u1, u2;
fill(&u1);
u2 = u1; // which u2 member is created here?
Run Code Online (Sandbox Code Playgroud)
当在填充memcpy有数值的字节缓冲区(不是从另一个联合对象填充)中使用联合时,哪个成员变为“活动”?
char buf[sizeof(U)] = { ... };
U u;
memcpy(&u, buf, sizeof u);
Run Code Online (Sandbox Code Playgroud) 这是你不应该做的场景,但https://timsong-cpp.github.io/cppwp/class.cdtor#4指出:
成员函数,包括虚函数([class.virtual]),可以在构造或销毁([class.base.init])期间调用。
如果并行调用这些函数,这是否成立?也就是说,忽略竞争条件,如果A正在构造过程中,并且frobme在构造函数被调用之后的某个时间点被调用(例如在构造过程中),那仍然是定义的行为吗?
#include <thread>
struct A {
void frobme() {}
};
int main() {
char mem[sizeof(A)];
auto t1 = std::thread([mem]() mutable { new(mem) A; });
auto t2 = std::thread([mem]() mutable { reinterpret_cast<A*>(mem)->frobme(); });
t1.join();
t2.join();
}
Run Code Online (Sandbox Code Playgroud)
作为一个单独的场景,有人还向我指出, 的A构造函数可以创建多个线程,这些线程可能会A在构造完成之前调用成员函数函数,但是这些操作的顺序会更易于分析(您知道在构造函数中生成线程之后才会发生竞争)。
c++ constructor race-condition object-lifetime language-lawyer
另一个问题引用了C++标准:
3.8/1 “类型 T 的对象的生命周期在以下情况下结束: — 如果 T 是具有非平凡析构函数的类类型 (12.4),则析构函数调用开始,或者 — 对象占用的存储空间被重用或释放。 ”
这似乎意味着不允许从析构函数访问对象的成员。然而,这似乎是错误的,事实更像是 Kerrek SB 的回答中所解释的:
成员对象在构造函数体运行之前活跃起来,并且它们一直活跃到析构函数完成之后。因此,您可以在构造函数和析构函数中引用成员对象。
对象本身直到它自己的构造函数完成后才会活跃起来,并且一旦它的析构函数开始执行它就会死亡。但这仅就外部世界而言。构造函数和析构函数仍然可以引用成员对象。
我想知道在析构函数中是否可以将对象的地址传递给外部类,例如:
struct Person;
struct Organizer
{
static void removeFromGuestList(const Person& person); // This then accesses Person members
}
struct Person
{
~Person() {
// I'm about to die, I won't make it to the party
Organizer::removeFromGuestList(*this);
}
};
Run Code Online (Sandbox Code Playgroud)
这对我来说似乎没问题,因为我认为对象的生命周期一直持续到析构函数完成之后,但是上述答案的这一部分让我怀疑:
对象本身直到它自己的构造函数完成后才会活跃起来,并且一旦它的析构函数开始执行它就会死亡。但这仅就外部世界而言。构造函数和析构函数仍然可以引用成员对象。
根据标准:
5.13.5 字符串文字 [lex.string]
16 评估字符串文字会产生具有静态存储持续时间的字符串文字对象,该对象根据上面指定的给定字符进行初始化。未指定所有字符串文字是否不同(即存储在不重叠的对象中)以及字符串文字的连续计算是否产生相同或不同的对象。
和:
6.6.4.1 静态存储持续时间[basic.stc.static]
1 所有没有动态存储持续时间、没有线程存储持续时间、并且不是本地的变量都具有静态存储持续时间。这些实体的存储应在程序持续期间持续
我认为存储指向字符串文字的指针是安全的,例如:
struct Base
{
Base(const char* name)
: _name(name)
{
}
void print()
{
std::cout<<_name<<std::endl;
}
const char* _name = nullptr;
};
struct MyDerived : public Base
{
MyDerived () : Base("MyDerived")
{
}
};
Run Code Online (Sandbox Code Playgroud)
上面的代码定义清楚了吗?标准中有哪些我必须注意的阴暗角落吗?
一种编程语言是否有可能始终具有 Rust 风格的所有权和生命周期(用于自动内存管理),同时放弃对一段数据在任何时间只能存在一个可变引用的要求(用于抑制数据竞争)?
换句话说,Rust 风格的所有权和生命周期以及 Rust 风格的借用检查是两个独立的概念吗?或者,这两个想法在语义层面上本质上是纠缠在一起的吗?
memory-management lifetime object-lifetime rust borrow-checker
假设X和是适合这种用途的类型,那么在一个线程中的内存区域上Y使用作为一种类型并在另一个线程中的完全相同的内存上使用是否是UB?标准对此有什么规定吗?如果不正确,正确的解释是什么?std::start_lifetime_as<X>std::start_lifetime_as<Y>
c++ object-lifetime undefined-behavior language-lawyer c++23
让我声明:我对构造函数或析构函数中的虚函数调用有清楚的理解.
在下面的代码中,我试图避免虚拟析构函数仅用于实验目的.
现在我的问题是:
主要是对Destroy fun的调用调用正确的虚函数.我期待任何对Destroy Function的调用都应该调用正确的虚拟乐趣.
但是同样的Destroy函数放在Base析构函数调用的Base虚函数中.
这与静态绑定或编译器优化有关吗?
class Base
{
public:
Base()
{
}
void Destroy()
{
callVirtual();
}
virtual void callVirtual()
{
cout<<"In Base callVirtual "<<endl;
}
~ Base()
{
cout<<"In Base Destructor"<<endl;
Destroy();
}
};
Run Code Online (Sandbox Code Playgroud)
.
class Derived : public Base
{
public:
Derived()
{
}
void callVirtual()
{
cout"<<In Derived callVirtual"<<endl;
}
};
Run Code Online (Sandbox Code Playgroud)
.
int main()
{
Base *pointer = new Derived();
pointer->Destroy(); // Calls the right callVirtual
return 0;
}
Run Code Online (Sandbox Code Playgroud) 从ildjarn读完这个答案之后,我编写了以下示例,它看起来像一个未命名的临时对象与其引用具有相同的生命周期!
源代码:
#include <iostream> //cout
#include <sstream> //ostringstream
int main ()
{
std::ostringstream oss;
oss << 1234;
std::string const& str = oss.str();
char const* ptr = str.c_str();
// Change the stream content
oss << "_more_stuff_";
oss.str(""); //reset
oss << "Beginning";
std::cout << oss.str() <<'\n';
// Fill the call stack
// ... create many local variables, call functions...
// Change again the stream content
oss << "Again";
oss.str(""); //reset
oss << "Next should …Run Code Online (Sandbox Code Playgroud) 当执行行超出代码块之外时,变量会发生什么?例如:
1 public void myMethod()
2 {
3 int number;
4 number = 5;
5 }
Run Code Online (Sandbox Code Playgroud)
因此,我们声明并设置变量。当它超出代码块(第5行)时,变量号发生了什么?
这是创建类实例的另一个示例:
7 public void myMethod()
8 {
9 Customer myClient;
10 myClient = new Customer();
11 }
Run Code Online (Sandbox Code Playgroud)
当它超出代码块(第11行)时,对象引用myClient会发生什么?
我猜这两种情况都分配了变量,但是何时将其释放呢?
我想知道是否偶然指向绑定到已销毁堆栈变量的const引用的指针可以正常工作.
我读取const引用生命周期是在rvalues上扩展,所以这是"正常"的const引用工作,但是在存储引用的ctor的末尾应该被销毁,不是吗?
const引用生命周期是否也被扩展,因为我在指针中检索它的地址或者这是纯粹的运气?
#include <iostream>
class Storage
{
public:
Storage(const int& ref)
{
p = &ref;
}
const int* Get() const
{
return p;
}
private:
const int* p;
};
int main()
{
Storage* s = nullptr;
{
int someValue = 42;
std::cout << &someValue << std::endl;
s = new Storage(someValue);
}
const int* p = s->Get();
std::cout << p << std::endl;
std::cout << *p << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
它也使用struct Live示例
#include <iostream>
struct Dummy
{ …Run Code Online (Sandbox Code Playgroud) object-lifetime ×10
c++ ×8
lifetime ×2
reference ×2
c# ×1
c++23 ×1
constructor ×1
destructor ×1
memcpy ×1
overriding ×1
rust ×1
static ×1
temporary ×1
unions ×1