我们能胜过对象切片吗?



免责声明:只有当你想看看它是如何完成的时,才继续阅读。显然这是错误的,当你看到它时,你会从中得上眼癌(我是免疫的,因为我是一个菜鸟:)


所以我在考虑C++运行时多态性一段时间了,我想出了以下内容。考虑一下

#include <iostream>
struct Animal
{
virtual ~Animal(){}
void make_noise() const {return do_make_noise();}
protected:
Animal( ){}
virtual void do_make_noise()const{std::cout << "Mehn";}
};
struct Cat:public Animal
{
void do_make_noise()const{ std::cout<< "meown";}
};
struct Dog:public Animal
{
void do_make_noise()const{ std::cout<< "woofn";}
};
int main()
{
Cat cat;
Dog dog;
Animal a((const Animal&)cat); //every tutorial teaches us that this is bad!!
a.make_noise();
a = (const Animal&)dog;
a.make_noise();
return 0;
}

如您所料,输出是

Meh
Meh

好的,对象切片很糟糕:/但是
现在让我稍微更改一下 Base 类:

struct Animal
{
virtual ~Animal(){}
void make_noise() const {return ptr_->do_make_noise();}
protected:
Animal( ):ptr_(this){}
Animal* ptr_;
virtual void do_make_noise()const{std::cout << "Mehn";}
};

那么结果是

meow
woof

哈哈。螺丝物体切片机
对此,你怎么看?在我看来,能够按值复制并仍然获得多态行为具有许多优势。 人们不使用它有什么原因吗?还是我只是在这里重新发明轮子?

编写的代码没有表现出未定义的行为,并且确实"阻止了对象切片";相反,它非常脆弱且无法维护。

现在,每个Animal都有一个非托管指针,指向它充当的某个动物(有时是它自己(。 您的代码违反了合理的复制和移动构造的含义。

实际上,它避免了崩溃和未定义的行为,但只是偶然的。 看似无害的小调整和程序中断。

编写一个返回动物的函数? 您的代码会中断,具体取决于省略。 从本地动物复制到传入的引用,然后返回? 破碎。 把它们放在标准病载体中? 彻底的灾难。

创建不遭受切片的价值语义多态类型看起来有点像这样,但是您将虚拟层次结构与拥有 pImpl 的值类型分开,并且实例拥有pImpl 指向的内容。

struct IAnimal{
virtual void speak() const=0;
virtual std::unique_ptr<IAnimal> clone() const = 0;
virtual ~IAnimal(){}
};
struct Animal {
std::unique_ptr<IAnimal> pImpl;
void speak() const { if (pImpl) pImpl->speak(); }
explicit operator bool()const{return (bool)pImpl;}
Animal(Animal&&)=default;
Animal& operator=(Animal&&)=default;
Animal(Animal const&o):
Animal(o.pImpl?Animal(o.pImpl->clone()):Animal())
{}
Animal()=default;
Animal& operator=(Animal const& o){
Animal tmp(o);
swap(pImpl, tmp.pImpl);
return *this;
}
Animal(std::unique_ptr<IAnimal> x):pImpl(std::move(x)){}
};

现在Animal不能切片,可以是多态的。

struct Dog:Animal{
struct IDog:IAnimal{
std::unique_ptr<IAnimal> clone()const final override{ return std::make_unique<IDog>(*this); }
void speak()const final override { std:cout <<"woofn"; }
};
Dog():Animal(std::make_unique<IDog>()){}
};  
struct Cat:Animal{
struct ICat:IAnimal{
std::unique_ptr<IAnimal> clone()const final override{ return std::make_unique<ICat>(*this); }
void speak()const final override { std:cout <<"meown"; }
};
Cat():Animal(std::make_unique<ICat>()){}
};

除非 tpyos,否则我们会得到:

Cat cat;
Dog dog;
Animal a((const Animal&)cat); //every tutorial teaches us that this is bad!!
a.speak();
a = (const Animal&)dog;
a.speak();

加工。

而且我的复制/移动 ctor 是明智的,我在向量中工作,而且我没有到处都是悬空的指针。

相关内容

  • 没有找到相关文章

最新更新