当我在C#编译器遇到一些非常奇怪的代码时,我正在回答关于闭包(合法地)延长对象生命周期的可能性的问题(如果重要的话,那就是4.0).
我能找到的最短的repro如下:
结果:编译器创建一个闭包对象,该对象引用创建lambda的对象,当它没有理由时 - 委托的"内部"目标是静态方法,并且lambda-creation-object的实例成员不需要在委托执行时被(并且没有)触摸.实际上,编译器的行为就像程序员this无缘无故地捕获的那样.
class Foo
{
private Action _field;
public void InstanceMethod()
{
var capturedVariable = Math.Pow(42, 1);
_field = () => StaticMethod(capturedVariable);
}
private static void StaticMethod(double arg) { }
}
Run Code Online (Sandbox Code Playgroud)
发布版本生成的代码(反编译为'simpler'C#)如下所示:
public void InstanceMethod()
{
<>c__DisplayClass1 CS$<>8__locals2 = new <>c__DisplayClass1();
CS$<>8__locals2.<>4__this = this; // What's this doing here?
CS$<>8__locals2.capturedVariable = Math.Pow(42.0, 1.0);
this._field = new Action(CS$<>8__locals2.<InstanceMethod>b__0);
}
[CompilerGenerated]
private sealed class …Run Code Online (Sandbox Code Playgroud) 什么时候C++中的对象被破坏了,这意味着什么?我是否必须手动销毁它们,因为没有垃圾收集器?例外是如何发挥作用的?
(注意:这是Stack Overflow的C++常见问题解答的一个条目.如果你想批评在这种形式下提供常见问题解答的想法,那么发布所有这些的元数据的发布将是这样做的地方.这个问题在C++聊天室中受到监控,其中FAQ的想法一开始就出现了,所以你的答案很可能被那些提出想法的人阅读.)
来自http://en.cppreference.com/w/cpp/string/byte/memcpy:
如果对象不是TriviallyCopyable(例如标量,数组,C兼容结构),则行为未定义.
在我的工作中,我们使用std::memcpy了很长时间来按比例交换不是TriviallyCopyable的对象:
void swapMemory(Entity* ePtr1, Entity* ePtr2)
{
static const int size = sizeof(Entity);
char swapBuffer[size];
memcpy(swapBuffer, ePtr1, size);
memcpy(ePtr1, ePtr2, size);
memcpy(ePtr2, swapBuffer, size);
}
Run Code Online (Sandbox Code Playgroud)
从来没有任何问题.
我理解滥用std::memcpy非TriviallyCopyable对象并导致下游的未定义行为是微不足道的.但是,我的问题是:
std::memcpy当与非TriviallyCopyable对象一起使用时,为什么它本身的行为是未定义的?为什么标准认为有必要指定?
UPDATE
http://en.cppreference.com/w/cpp/string/byte/memcpy的内容已经过修改,以回应这篇文章和帖子的答案.目前的描述说:
如果对象不是TriviallyCopyable(例如标量,数组,C兼容结构),则行为是未定义的,除非程序不依赖于目标对象(不运行
memcpy)的析构函数的效果和生命周期目标对象(已结束,但未开始memcpy)由其他一些方法启动,例如placement-new.
PS
@Cubbi的评论:
@RSahu如果有东西保证UB下游,它会使整个程序不确定.但我同意在这种情况下似乎可以绕过UB并相应地修改cppreference.
这似乎是一个愚蠢的问题,但是return xxx;在一个明确定义的函数中"执行" 的确切时刻?
请参阅以下示例以了解我的意思(现在直播):
#include <iostream>
#include <string>
#include <utility>
//changes the value of the underlying buffer
//when destructed
class Writer{
public:
std::string &s;
Writer(std::string &s_):s(s_){}
~Writer(){
s+="B";
}
};
std::string make_string_ok(){
std::string res("A");
Writer w(res);
return res;
}
int main() {
std::cout<<make_string_ok()<<std::endl;
}
Run Code Online (Sandbox Code Playgroud)
我天真地期待发生的事情make_string_ok被称为:
res被调用(价值res就是"A")w调用构造函数return res被执行.应该返回res的当前值(通过复制当前值res),即"A".w被称为析构函数,值res变为"AB".res函数被称为.所以我希望"A"结果,但"AB" …
当MarshalByRef对象从AppDomain(1)传递到另一个(2)时,如果你在第二个AppDomain(2)中调用方法之前等待6分钟,你将得到一个RemotingException:
System.Runtime.Remoting.RemotingException:对象[...]已断开连接或在服务器上不存在.
有关此问题的一些文档:
如果我错了,请纠正我:如果InitializeLifetimeService返回null,那么当AppDomain 2被卸载时,该对象只能在AppDomain 1中收集,即使收集了代理?
有没有办法禁用生命周期并保持代理(在AppDomain 2中)和对象(在AppDomain1中)保持活动状态,直到代理完成为止?也许与ISponsor ......?
我有以下代码:
#include <stdexcept>
#include <iostream>
struct ok {
int _n;
ok(int n) : _n(n) { std::cerr << "OK" << n << " born" << std::endl; }
~ok() { std::cerr << "OK" << _n << " gone" << std::endl; }
};
struct problematic {
~problematic() noexcept(false) { throw std::logic_error("d-tor exception"); }
};
ok boo() {
ok ok1{1};
problematic p;
ok ok2{2};
return ok{3}; // Only constructor is called...
}
int main(int argc, char **argv) {
try {boo();} catch(...) {}
}
Run Code Online (Sandbox Code Playgroud)
我看到他没有调用ok …
根据这个答案,我现在想知道lambda的生命周期是什么规则,以及与自动转换创建的函数指针的生命周期有什么关系.关于lambda的生命周期有几个问题(例如这里和这里),在这种情况下,答案是"它们的行为与你自己编写完整的仿函数对象完全一样",但是它们都没有解决转换为函数指针的问题.特殊情况.
我把这个小小的工作实例放在一起,说明了我的担忧:
#include <iostream>
typedef int (*func_t)(int);
// first case
func_t retFun1() {
static auto lambda = [](int) { return 1; };
// automatically converted to func_t
return lambda;
}
// second case
func_t retFun2() {
// no static
auto lambda = [](int) { return 2; };
// automatically converted to func_t and
// the local variable lambda reaches the end of its life
return lambda;
}
int main() {
const int a = …Run Code Online (Sandbox Code Playgroud) 我有一个包含纯虚函数的基类MyBase:
void PrintStartMessage() = 0
我希望每个派生类在它们的构造函数中调用它
然后我把它放在基类(MyBase)构造函数中
class MyBase
{
public:
virtual void PrintStartMessage() =0;
MyBase()
{
PrintStartMessage();
}
};
class Derived:public MyBase
{
public:
void PrintStartMessage(){
}
};
void main()
{
Derived derived;
}
Run Code Online (Sandbox Code Playgroud)
但我收到链接器错误.
this is error message :
1>------ Build started: Project: s1, Configuration: Debug Win32 ------
1>Compiling...
1>s1.cpp
1>Linking...
1>s1.obj : error LNK2019: unresolved external symbol "public: virtual void __thiscall MyBase::PrintStartMessage(void)" (?PrintStartMessage@MyBase@@UAEXXZ) referenced in function "public: __thiscall MyBase::MyBase(void)" (??0MyBase@@QAE@XZ)
1>C:\Users\Shmuelian\Documents\Visual Studio 2008\Projects\s1\Debug\s1.exe : fatal …Run Code Online (Sandbox Code Playgroud) 如果某些函数f带有参数p_1,...,p_n类型T_1,...,T_n分别用参数调用a_1,......,a_n并且它的正文抛出异常,则完成或返回,参数被破坏的顺序是什么?为什么?如果可能,请提供标准参考.
编辑:我实际上想询问函数"参数",但是由于TC和Columbo设法清除了我的困惑,我将这个问题留下来讨论参数并询问一个关于参数的新单独问题.有关区别,请参阅有关此问题的评论.
我的一个朋友给我看了一个 C++20 程序:
#include <iostream>
struct A
{
A() {std::cout << "A()\n";}
~A() {std::cout << "~A()\n";}
};
struct B
{
const A &a;
};
int main()
{
B x({});
std::cout << "---\n";
B y{{}};
std::cout << "---\n";
B z{A{}};
std::cout << "---\n";
}
Run Code Online (Sandbox Code Playgroud)
在 GCC 中,它打印:
A()
~A()
---
A()
---
A()
---
~A()
~A()
Run Code Online (Sandbox Code Playgroud)
https://gcc.godbolt.org/z/ce3M3dPeo
因此,A在 y 和 z 情况下,的寿命会延长。
在 Visual Studio 中,结果是不同的:
A()
~A()
---
A()
---
A()
~A()
---
~A()
Run Code Online (Sandbox Code Playgroud)
所以A只有在 y …
object-lifetime ×10
c++ ×8
c# ×2
c++11 ×2
destructor ×2
exception ×2
lambda ×2
.net ×1
appdomain ×1
arguments ×1
c++-faq ×1
c++14 ×1
c++17 ×1
c++20 ×1
closures ×1
constructor ×1
copy-elision ×1
function ×1
memcpy ×1
memory-leaks ×1
pure-virtual ×1
remoting ×1