抽象类型的复制构造数据成员
Copy-constructing data members of abstract type
考虑以下场景:
class AbsBase {
public:
virtual double foo() = 0;
};
class Concrete1 : public AbsBase {
public:
double foo() {
return 1;
}
};
class Concrete2 : public AbsBase {
public:
double foo() {
return 2;
}
};
struct Config {
/**
* Must use a pointer to `AbsBase`, since it is an abstract base class,
* and the concrete instances could be of either of the derived classes.
*/
AbsBase* thing;
void setThing(AbsBase* t) {
thing = t;
}
};
这个 有效 ,但如果 thing
是传递给 setThing
的参数的副本,我会更喜欢它。 IE。如果 thing
是 Concrete1
类型(与抽象基础 class 相对),我可以这样做:
Concrete1 thing;
void setThing(const Concrete1& t) {
thing = Concrete1(t);
}
但是,如果不实例化抽象类型的对象(这是非法的),我看不到实现此目的的方法。有什么想法吗?
除了使用 clone
方法的 duplicate 之外,在您的情况下,您还可以通过模板接受具体类型。
struct Config {
AbsBase* thing;
template <typename T>
void setThing(T& t) {
thing = new T(t);
}
};
您可以使用模板函数来实现所需的行为。
我建议也使用 std::unique_ptr
来防止内存问题。
class AbsBase {
public:
virtual ~AbsBase() = default;
virtual double foo() = 0;
};
class Concrete1 : public AbsBase {
public:
double foo() {
return 1;
}
};
class Concrete2 : public AbsBase {
public:
double foo() {
return 2;
}
};
struct Config {
/**
* Must use a pointer to `AbsBase`, since it is an abstract base class,
* and the concrete instances could be of either of the derived classes.
*/
std::unique_ptr<AbsBase> thing;
template <typename T>
void setThing(const T& t) {
static_assert(std::is_base_of<AbsBase, T>::value, "T must inherit AbsBase");
static_assert(std::is_copy_constructible<T>::value, "T must be copy-constructible");
thing.reset(new T{t});
}
void test()
{
std::cout << (thing?thing->foo():0.0) << std::endl;
}
};
这里有一个例子(std::cout
更容易理解)。不要忘记创建 AbsBase
的虚拟析构函数以避免内存泄漏。
#include <iostream>
class AbsBase {
public:
virtual double foo() = 0;
virtual ~AbsBase() {
std::cout << "destructor AbsBase" << std::endl;
}
};
class Concrete1 : public AbsBase {
public:
Concrete1() {
std::cout << "default constructor of Concrete1" << std::endl;
}
Concrete1(const Concrete1 & other) {
std::cout << "copy constructor of Concrete1" << std::endl;
}
Concrete1 & operator=(const Concrete1 & other) {
std::cout << "assignment operator of Concrete1" << std::endl;
}
virtual ~Concrete1() {
std::cout << "destructor of Concrete1" << std::endl;
}
double foo() {
return 1;
}
};
class Concrete2 : public AbsBase {
public:
Concrete2() {
std::cout << "default constructor of Concrete2" << std::endl;
}
Concrete2(const Concrete2 & other) {
std::cout << "copy constructor of Concrete2" << std::endl;
}
Concrete2 & operator=(const Concrete2 & other) {
std::cout << "assignment operator of Concrete2" << std::endl;
}
virtual ~Concrete2() {
std::cout << "destructor of Concrete2" << std::endl;
}
double foo() {
return 2;
}
};
class Config {
private:
/**
* Must use a pointer to `AbsBase`, since it is an abstract base class,
* and the concrete instances could be of either of the derived classes.
*/
AbsBase* thing;
public:
Config() : thing(0) {
std::cout << "constructor of Config" << std::endl;
}
~Config() {
std::cout << "destructor of Config" << std::endl;
if (thing) {
std::cout << "delete thing" << std::endl;
delete thing;
}
}
template<typename T>
void setThing(T & t) {
std::cout << "setThing" << std::endl;
if (thing) {
std::cout << "delete thing" << std::endl;
delete thing;
}
thing = new T (t);
}
AbsBase* getThing() {
return thing;
}
};
int main() {
Config config;
Concrete1 concrete1;
Concrete2 concrete2;
std::cout << "=================" << std::endl;
std::cout << "save concrete1" << std::endl;
config.setThing(concrete1);
std::cout << config.getThing()-> foo() << std::endl;
std::cout << "=================" << std::endl;
std::cout << "save concrete2" << std::endl;
config.setThing(concrete2);
std::cout << config.getThing()-> foo() << std::endl;
std::cout << "=================" << std::endl;
std::cout << "destruction of all local variables" << std::endl;
return 0;
}
输出:
constructor of Config
default constructor of Concrete1
default constructor of Concrete2
=================
save concrete1
setThing
copy constructor of Concrete1
1
=================
save concrete2
setThing
delete thing
destructor of Concrete1
destructor AbsBase
copy constructor of Concrete2
2
=================
destruction of all local variables
destructor of Concrete2
destructor AbsBase
destructor of Concrete1
destructor AbsBase
destructor of Config
delete thing
destructor of Concrete2
destructor AbsBase
考虑以下场景:
class AbsBase {
public:
virtual double foo() = 0;
};
class Concrete1 : public AbsBase {
public:
double foo() {
return 1;
}
};
class Concrete2 : public AbsBase {
public:
double foo() {
return 2;
}
};
struct Config {
/**
* Must use a pointer to `AbsBase`, since it is an abstract base class,
* and the concrete instances could be of either of the derived classes.
*/
AbsBase* thing;
void setThing(AbsBase* t) {
thing = t;
}
};
这个 有效 ,但如果 thing
是传递给 setThing
的参数的副本,我会更喜欢它。 IE。如果 thing
是 Concrete1
类型(与抽象基础 class 相对),我可以这样做:
Concrete1 thing;
void setThing(const Concrete1& t) {
thing = Concrete1(t);
}
但是,如果不实例化抽象类型的对象(这是非法的),我看不到实现此目的的方法。有什么想法吗?
除了使用 clone
方法的 duplicate 之外,在您的情况下,您还可以通过模板接受具体类型。
struct Config {
AbsBase* thing;
template <typename T>
void setThing(T& t) {
thing = new T(t);
}
};
您可以使用模板函数来实现所需的行为。
我建议也使用 std::unique_ptr
来防止内存问题。
class AbsBase {
public:
virtual ~AbsBase() = default;
virtual double foo() = 0;
};
class Concrete1 : public AbsBase {
public:
double foo() {
return 1;
}
};
class Concrete2 : public AbsBase {
public:
double foo() {
return 2;
}
};
struct Config {
/**
* Must use a pointer to `AbsBase`, since it is an abstract base class,
* and the concrete instances could be of either of the derived classes.
*/
std::unique_ptr<AbsBase> thing;
template <typename T>
void setThing(const T& t) {
static_assert(std::is_base_of<AbsBase, T>::value, "T must inherit AbsBase");
static_assert(std::is_copy_constructible<T>::value, "T must be copy-constructible");
thing.reset(new T{t});
}
void test()
{
std::cout << (thing?thing->foo():0.0) << std::endl;
}
};
这里有一个例子(std::cout
更容易理解)。不要忘记创建 AbsBase
的虚拟析构函数以避免内存泄漏。
#include <iostream>
class AbsBase {
public:
virtual double foo() = 0;
virtual ~AbsBase() {
std::cout << "destructor AbsBase" << std::endl;
}
};
class Concrete1 : public AbsBase {
public:
Concrete1() {
std::cout << "default constructor of Concrete1" << std::endl;
}
Concrete1(const Concrete1 & other) {
std::cout << "copy constructor of Concrete1" << std::endl;
}
Concrete1 & operator=(const Concrete1 & other) {
std::cout << "assignment operator of Concrete1" << std::endl;
}
virtual ~Concrete1() {
std::cout << "destructor of Concrete1" << std::endl;
}
double foo() {
return 1;
}
};
class Concrete2 : public AbsBase {
public:
Concrete2() {
std::cout << "default constructor of Concrete2" << std::endl;
}
Concrete2(const Concrete2 & other) {
std::cout << "copy constructor of Concrete2" << std::endl;
}
Concrete2 & operator=(const Concrete2 & other) {
std::cout << "assignment operator of Concrete2" << std::endl;
}
virtual ~Concrete2() {
std::cout << "destructor of Concrete2" << std::endl;
}
double foo() {
return 2;
}
};
class Config {
private:
/**
* Must use a pointer to `AbsBase`, since it is an abstract base class,
* and the concrete instances could be of either of the derived classes.
*/
AbsBase* thing;
public:
Config() : thing(0) {
std::cout << "constructor of Config" << std::endl;
}
~Config() {
std::cout << "destructor of Config" << std::endl;
if (thing) {
std::cout << "delete thing" << std::endl;
delete thing;
}
}
template<typename T>
void setThing(T & t) {
std::cout << "setThing" << std::endl;
if (thing) {
std::cout << "delete thing" << std::endl;
delete thing;
}
thing = new T (t);
}
AbsBase* getThing() {
return thing;
}
};
int main() {
Config config;
Concrete1 concrete1;
Concrete2 concrete2;
std::cout << "=================" << std::endl;
std::cout << "save concrete1" << std::endl;
config.setThing(concrete1);
std::cout << config.getThing()-> foo() << std::endl;
std::cout << "=================" << std::endl;
std::cout << "save concrete2" << std::endl;
config.setThing(concrete2);
std::cout << config.getThing()-> foo() << std::endl;
std::cout << "=================" << std::endl;
std::cout << "destruction of all local variables" << std::endl;
return 0;
}
输出:
constructor of Config
default constructor of Concrete1
default constructor of Concrete2
=================
save concrete1
setThing
copy constructor of Concrete1
1
=================
save concrete2
setThing
delete thing
destructor of Concrete1
destructor AbsBase
copy constructor of Concrete2
2
=================
destruction of all local variables
destructor of Concrete2
destructor AbsBase
destructor of Concrete1
destructor AbsBase
destructor of Config
delete thing
destructor of Concrete2
destructor AbsBase