我试图序列化具有指向多态成员m_parent
的指针的Container
类。问题是序列化是在Parent
类上完成的,而不是在ChildA
类上,并且我编写的测试程序打印了一个空的m_parent
。
我不明白为什么它不工作。我做错了什么?
#include <iostream>
#include <vector>
#include <memory>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <boost/serialization/export.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/unique_ptr.hpp>
class Parent
{
public:
Parent();
virtual ~Parent() = default;
virtual std::string print() const;
friend std::ostream& operator<<(std::ostream& out, const Parent& Parent);
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
}
};
Parent::Parent()
{
}
std::string Parent::print() const
{
return {};
}
std::ostream& operator<<(std::ostream& out, const Parent& Parent)
{
return out << Parent.print();
}
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Parent)
class ChildA : public Parent
{
private:
double m_value;
public:
ChildA() {};
ChildA(double value);
std::string print() const override;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar.template register_type<ChildA>();
ar & boost::serialization::base_object<Parent>(*this);
ar & m_value;
}
};
ChildA::ChildA(double value) : m_value(value)
{
}
std::string ChildA::print() const
{
return std::to_string(m_value);
}
BOOST_CLASS_EXPORT(ChildA)
class Container
{
private:
unsigned long m_id;
std::unique_ptr<Parent> m_parent;
public:
Container() {};
Container(unsigned long id, std::unique_ptr<Parent> parent);
Container(const Container& container);
friend std::ostream& operator<<(std::ostream& out, const Container& container);
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar.template register_type<ChildA>();
ar & m_id & m_parent;
}
};
Container::Container(unsigned long id, std::unique_ptr<Parent> parent) : m_id(id), m_parent(std::move(parent))
{
}
Container::Container(const Container& container): m_id(container.m_id), m_parent(std::make_unique<Parent>((*container.m_parent)))
{
}
std::ostream& operator<<(std::ostream& out, const Container& container)
{
return out << container.m_id << "," << *container.m_parent;
}
BOOST_CLASS_EXPORT(Container)
int main()
{
Container container_1(1, std::make_unique<ChildA>(2.3));
std::cout << container_1 << std::endl;
std::vector<std::unique_ptr<Container>> b;
b.push_back(std::make_unique<Container>(container_1));
boost::filesystem::path file = boost::filesystem::current_path() / "test.dat";
if (boost::filesystem::exists(file))
{
boost::filesystem::ifstream ifs(file);
boost::archive::text_iarchive ta(ifs);
ta & b;
std::cout << *b[0] << std::endl;
}
else
{
boost::filesystem::ofstream ofs(file);
boost::archive::text_oarchive ta(ofs);
ta & b;
}
}
您已经将默认构造的Parent
编写为m_parent
:
Live On Coliru
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <iostream>
#include <memory>
#include <vector>
class Parent {
public:
Parent() = default;
virtual ~Parent() = default;
virtual std::string print() const { return {}; }
friend class boost::serialization::access;
template <class Archive> void serialize(Archive&, unsigned) {}
friend std::ostream& operator<<(std::ostream& out, Parent const& Parent) { return out << Parent.print(); }
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Parent)
class ChildA : public Parent {
double m_value;
public:
ChildA(double value = 0.0) : m_value(value) {}
std::string print() const override { return std::to_string(m_value); }
friend class boost::serialization::access;
template <class Archive> void serialize(Archive& ar, unsigned) {
// ar.template register_type<ChildA>();
ar & boost::serialization::base_object<Parent>(*this) & m_value;
}
};
BOOST_CLASS_EXPORT(ChildA)
class Container {
private:
unsigned long m_id;
std::unique_ptr<Parent> m_parent;
public:
Container(unsigned long id = 0, std::unique_ptr<Parent> parent = {})
: m_id(id)
, m_parent(std::move(parent)) {}
Container(Container const& container)
: m_id(container.m_id)
, m_parent(std::make_unique<Parent>((*container.m_parent))) {}
friend std::ostream& operator<<(std::ostream& out, Container const& container) {
return out << container.m_id << "," << *container.m_parent;
}
friend class boost::serialization::access;
template <class Archive> void serialize(Archive& ar, unsigned) {
ar & m_id & m_parent;
}
};
// BOOST_CLASS_EXPORT(Container)
int main() {
boost::filesystem::path file = "test.dat";
if (exists(file))
remove(file);
while(true) {
if (exists(file)) {
std::vector<std::unique_ptr<Container>> b;
{
boost::filesystem::ifstream ifs(file);
boost::archive::text_iarchive ta(ifs);
ta >> b;
}
std::cout << file << " loaded: " << *b.front() << std::endl;
break; // exits program
} else {
Container container_1(23, std::make_unique<ChildA>(2.3));
std::vector<std::unique_ptr<Container>> b;
b.push_back(std::make_unique<Container>(container_1));
{
boost::filesystem::ofstream ofs(file);
boost::archive::text_oarchive ta(ofs);
ta << b;
}
std::cout << file << " written: " << *b.front() << std::endl;
}
}
}
打印
"test.dat" written: 23,
"test.dat" loaded: 23,
问题?
问题在于复制构造函数。注意当替换
时Container container_1(23, std::make_unique<ChildA>(2.3));
std::vector<std::unique_ptr<Container>> b;
b.push_back(std::make_unique<Container>(container_1));
std::vector<std::unique_ptr<Container>> b;
b.push_back(std::make_unique<Container>(23, std::make_unique<ChildA>(2.3)));
输出变成预期的:
"test.dat" written: 23,2.300000
"test.dat" loaded: 23,2.300000
原因是
m_parent(std::make_unique<Parent>(*container.m_parent))
调用Parent::Parent(Parent const&)
复制构造函数——这显然不是虚拟的。使用clone
模式允许深度复制多态类型:
virtual std::unique_ptr<Parent> clone() const { return std::make_unique<Parent>(); }
然后重写,如:
virtual std::unique_ptr<Parent> clone() const override { return std::make_unique<ChildA>(m_value); }
现在可以通过修复复制构造函数来修复原始代码:
Container(Container const& rhs)
: m_id(rhs.m_id)
, m_parent(rhs.m_parent ? rhs.m_parent->clone() : nullptr) {}
请注意,我选择通过检查nullptr来使代码更安全。
查看Live On Coliru
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <iostream>
#include <memory>
#include <vector>
class Parent {
public:
Parent() = default;
virtual ~Parent() = default;
virtual std::string print() const { return {}; }
virtual std::unique_ptr<Parent> clone() const { return std::make_unique<Parent>(); }
friend class boost::serialization::access;
template <class Archive> void serialize(Archive&, unsigned) {}
friend std::ostream& operator<<(std::ostream& out, Parent const& Parent) { return out << Parent.print(); }
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Parent)
class ChildA : public Parent {
double m_value;
public:
ChildA(double value = 0.0) : m_value(value) {}
std::string print() const override { return std::to_string(m_value); }
virtual std::unique_ptr<Parent> clone() const override { return std::make_unique<ChildA>(m_value); }
friend class boost::serialization::access;
template <class Archive> void serialize(Archive& ar, unsigned) {
// ar.template register_type<ChildA>();
ar & boost::serialization::base_object<Parent>(*this) & m_value;
}
};
BOOST_CLASS_EXPORT(ChildA)
class Container {
private:
unsigned long m_id;
std::unique_ptr<Parent> m_parent;
public:
Container(unsigned long id = 0, std::unique_ptr<Parent> parent = {})
: m_id(id)
, m_parent(std::move(parent)) {}
Container(Container const& rhs)
: m_id(rhs.m_id)
, m_parent(rhs.m_parent ? rhs.m_parent->clone() : nullptr) {}
friend std::ostream& operator<<(std::ostream& out, Container const& container) {
return out << container.m_id << "," << *container.m_parent;
}
friend class boost::serialization::access;
template <class Archive> void serialize(Archive& ar, unsigned) {
ar & m_id & m_parent;
}
};
// BOOST_CLASS_EXPORT(Container)
int main() {
boost::filesystem::path file = "test.dat";
if (exists(file))
remove(file);
while(true) {
if (exists(file)) {
std::vector<std::unique_ptr<Container>> b;
{
boost::filesystem::ifstream ifs(file);
boost::archive::text_iarchive ta(ifs);
ta >> b;
}
std::cout << file << " loaded: " << *b.front() << std::endl;
break; // exits program
} else {
Container container_1(23, std::make_unique<ChildA>(2.3));
std::vector<std::unique_ptr<Container>> b;
b.push_back(std::make_unique<Container>(container_1));
{
boost::filesystem::ofstream ofs(file);
boost::archive::text_oarchive ta(ofs);
ta << b;
}
std::cout << file << " written: " << *b.front() << std::endl;
}
}
}
打印
"test.dat" written: 23,2.300000
"test.dat" loaded: 23,2.300000
注意,BOOST_CLASS_EXPORT(Container)不是必需的,因为它不是多态类型。此外,register_type
调用是冗余的,类型被正确导出。