在指针转换期间初始化?
Initialization during a pointer cast?
如果我定义了一个派生的 class,它有初始化器,但最终使用指针转换(即 static_pointer_cast),我怎样才能在不执行取消引用的情况下执行初始化器,然后再执行一个对象复制?
#include <string>
#include <memory>
class Base {
public:
std::string Name;
};
class Derived : public Base {
public:
std::string Address = "Initialized";
};
int main() {
auto b_ptr = std::make_shared<Base>();
b_ptr->Name = "Fred";
auto d_ptr = std::static_pointer_cast<Derived>(b_ptr);
fprintf( stdout, "Name: [%s] Address: [%s]",
d_ptr->Name.c_str(),
d_ptr->Address.c_str() ); // Address not valid!
}
Link 编码:http://coliru.stacked-crooked.com/a/09f2240abff1556b
处理这个问题的正确方法是什么?
编辑:下面是一些示例代码(当然比真实的代码简化了很多)更好地说明了我正在尝试做的事情,以及如果有某种方法可以使这项工作更好的原因。
更新示例代码:http://coliru.stacked-crooked.com/a/cdcc31a4417bb52b
在这个例子中,我有两个数据源,一个来自源系统,另一个是内部使用的。我真的不想复制所有数据,只是在其中添加一些附加信息。
我不确定是否要使用 std::move(如@Mooing Duck 所建议),因为它对源数据有影响……我需要进一步探索。但是从这个例子来看,不执行复制的好处是显而易见的,不得不使用"has-a"风格的实现使得后续的对象使用变得尴尬。即:
test_row->Values[0].c_str() and
test_row->RowTotal
会变成:
test_row->row->Values[0] yet still
test_row->RowTotal
也许我看错了,有更好的算法吗?
你不能。成员的 brace-or-equal-initializer 仅由构造函数执行。如果要 Derived::Address
初始化,则需要实际构造一个 Derived
对象。但是你不能在已经被 Base
对象占用的 space 上构造一个 Derived
对象;你必须先销毁 Base
对象。
您不能就地更改类型。不是通过进行额外的初始化,或者礼貌地询问。不可能。
这个你问问自己就很容易看出来了
How much memory was allocated by make_shared<Base>
?
您可以给 Derived
一个 Base&&
构造函数,并使用它将 Base
的内容移动(浅拷贝)到派生中,从而留下原始的 Base
处于 "empty" 状态。
class Base {
public:
std::string Name;
//Note: The compiler is generating these invisibly for you:
//Base() :Name() {}
//~Base() {}
//Base(const Base& r) : Name(r.Name) {}
//Base(Base&& r) noexcept : Name(std::move(r.Name)) {}
//Base& operator=(const Base& r) : Name(r.Name) {}
//Base& operator=(Base&& r) noexcept : Name(std::move(r.Name)) {}
};
class Derived : public Base {
public:
std::string Address = "Initialized";
Derived() = default;
Derived(Base&& b) : Base(std::move(b)) {}
};
int main() {
auto b_ptr = std::make_shared<Base>();
b_ptr->Name = "Fred";
auto d_ptr = std::make_shared<Derived>(std::move(*b_ptr));
//NOTE AT THIS POINT b_ptr POINTS TO A BASE WHOS Name IS EMPTY
b_ptr.reset(); //reset to prevent accidental errors with lack of content
fprintf( stdout, "Name: [%s] Address: [%s]",
d_ptr->Name.c_str(),
d_ptr->Address.c_str() ); // Address not valid!
}
看到它在这里工作:http://coliru.stacked-crooked.com/a/f3a6062f6c459c7c
还可以在此处查看移动证明:http://coliru.stacked-crooked.com/a/f7f6cc4aa06d2746
但是,值得注意的是,我想不出您想要这样做的充分理由。听起来你的代码设计得很糟糕。
如果我定义了一个派生的 class,它有初始化器,但最终使用指针转换(即 static_pointer_cast),我怎样才能在不执行取消引用的情况下执行初始化器,然后再执行一个对象复制?
#include <string>
#include <memory>
class Base {
public:
std::string Name;
};
class Derived : public Base {
public:
std::string Address = "Initialized";
};
int main() {
auto b_ptr = std::make_shared<Base>();
b_ptr->Name = "Fred";
auto d_ptr = std::static_pointer_cast<Derived>(b_ptr);
fprintf( stdout, "Name: [%s] Address: [%s]",
d_ptr->Name.c_str(),
d_ptr->Address.c_str() ); // Address not valid!
}
Link 编码:http://coliru.stacked-crooked.com/a/09f2240abff1556b
处理这个问题的正确方法是什么?
编辑:下面是一些示例代码(当然比真实的代码简化了很多)更好地说明了我正在尝试做的事情,以及如果有某种方法可以使这项工作更好的原因。
更新示例代码:http://coliru.stacked-crooked.com/a/cdcc31a4417bb52b
在这个例子中,我有两个数据源,一个来自源系统,另一个是内部使用的。我真的不想复制所有数据,只是在其中添加一些附加信息。
我不确定是否要使用 std::move(如@Mooing Duck 所建议),因为它对源数据有影响……我需要进一步探索。但是从这个例子来看,不执行复制的好处是显而易见的,不得不使用"has-a"风格的实现使得后续的对象使用变得尴尬。即:
test_row->Values[0].c_str() and
test_row->RowTotal
会变成:
test_row->row->Values[0] yet still
test_row->RowTotal
也许我看错了,有更好的算法吗?
你不能。成员的 brace-or-equal-initializer 仅由构造函数执行。如果要 Derived::Address
初始化,则需要实际构造一个 Derived
对象。但是你不能在已经被 Base
对象占用的 space 上构造一个 Derived
对象;你必须先销毁 Base
对象。
您不能就地更改类型。不是通过进行额外的初始化,或者礼貌地询问。不可能。
这个你问问自己就很容易看出来了
How much memory was allocated by
make_shared<Base>
?
您可以给 Derived
一个 Base&&
构造函数,并使用它将 Base
的内容移动(浅拷贝)到派生中,从而留下原始的 Base
处于 "empty" 状态。
class Base {
public:
std::string Name;
//Note: The compiler is generating these invisibly for you:
//Base() :Name() {}
//~Base() {}
//Base(const Base& r) : Name(r.Name) {}
//Base(Base&& r) noexcept : Name(std::move(r.Name)) {}
//Base& operator=(const Base& r) : Name(r.Name) {}
//Base& operator=(Base&& r) noexcept : Name(std::move(r.Name)) {}
};
class Derived : public Base {
public:
std::string Address = "Initialized";
Derived() = default;
Derived(Base&& b) : Base(std::move(b)) {}
};
int main() {
auto b_ptr = std::make_shared<Base>();
b_ptr->Name = "Fred";
auto d_ptr = std::make_shared<Derived>(std::move(*b_ptr));
//NOTE AT THIS POINT b_ptr POINTS TO A BASE WHOS Name IS EMPTY
b_ptr.reset(); //reset to prevent accidental errors with lack of content
fprintf( stdout, "Name: [%s] Address: [%s]",
d_ptr->Name.c_str(),
d_ptr->Address.c_str() ); // Address not valid!
}
看到它在这里工作:http://coliru.stacked-crooked.com/a/f3a6062f6c459c7c
还可以在此处查看移动证明:http://coliru.stacked-crooked.com/a/f7f6cc4aa06d2746
但是,值得注意的是,我想不出您想要这样做的充分理由。听起来你的代码设计得很糟糕。