从工厂创建的实例访问派生 class 的成员
Accessing members of a derived class from a factory created instance
我有一个简单的 Shape
工厂示例,我可以在其中创建 Circle
或 Square
。
我在 Circle
class 中添加了一个额外的“contents
”属性,它不属于 Square
派生的 class 或 Shape
基础class.
问题是,当我使用我的工厂创建 Circle
class 的实例时,我无法修改创建对象的 contents
。
#include <iostream>
using namespace std;
// Shape base clas
class Shape {
public:
// Shape constructor;
Shape() {
id_ = total_++;
}
// Virtual draw method
virtual void draw() = 0;
protected:
int id_;
static int total_;
};
int Shape::total_ = 0;
// Circle derived class
class Circle : public Shape {
public:
void draw() {
contents = 0;
cout << "circle " << id_ << ": draw, contents: " << contents << endl;
}
// Attribute to attempt to access
int contents;
};
// Square derived class
class Square : public Shape {
public:
void draw() {
cout << "square " << id_ << ": draw" << endl;
}
};
// Factory class
class Factory {
public:
Shape* createCurvedInstance() {
return new Circle;
}
Shape* createStraightInstance() {
return new Square;
}
};
// Main
int main()
{
Factory* factory = new Factory;
Shape* thing = factory->createCurvedInstance();
// Draw method works fine (as it should)
thing->draw();
// Fails: "expression must have class type"
thing.contents = 4;
system("pause");
return 0;
}
当我使用工厂创建实例时,如何访问派生 class 的属性?
没办法,除非你施法,而你不得施法。多态背后的整个想法是它们通过不可变接口使自己可用的实例。他们强调了 IS-A 关系,其中 Circle 是所有意图和目的的 Shape,除了实现细节,没有人对此感兴趣。如果你公开添加 'contents' 到你的 Circle,它就不再是 Shape,所以它不应该通过工厂来建造。
由于 Shape
没有 content
,您不能将 content
从指针修改为 Shape
。句号。
但是,如果您知道您的特定 Shape
实际上是一个 Circle
并且有 content
,您可以转换为指向 Circle
的指针。
void set_content(Shape*shape, int content)
{
auto circle = dynamic_cast<Circle*>(shape);
if(circle)
circle->content = content;
}
这个版本特别安全:它不假设 shape
是 Circle*
,而是使用 dynamic_cast<Circle*>
,只有在 return 非空 shape
实际上是 Circle*
.
dynamic_cast<>
会产生一些费用,您可能希望避免这些费用。如果您有任何其他万无一失的方法来确定您的 shape
实际上是 Circle
,您可以使用简单的 static_cast<>
:
class Shape
{
public:
virtual bool has_content() const { return false; }
// ...
};
class ShapeWithContent : public Shape
{
public:
bool has_content() const override { return true; }
int content = 0;
};
class Circle : public ShapeWithContent
{
// ...
};
void set_content(Shape*shape, int content)
{
if(shape->has_content())
static_cast<ShapeWithContent*>(shape)->content = content;
}
说了这么多,我想强调的是,您应该尝试以制造此类技巧的方式设计代码 redundant/unnecessary。
原来我压根就不想建工厂!
我真正想要的是某种容器 class 来容纳不同类型的形状。这两种形状都不是从 ShapeContainer
class 派生的,但是可以通过它访问这两种形状。
#include <iostream>
using namespace std;
// Circle class
class Circle {
public:
Circle() {
contents = 2;
}
void draw() {
cout << "circle " << contents << endl;
}
int contents;
};
// Square class
class Square {
public:
void draw() {
cout << "square" << endl;
}
};
// Shape containter class
class ShapeContainer {
public:
Circle* getCircle() {
return new Circle;
}
Square* getSquare() {
return new Square;
};
};
// Main
int main()
{
ShapeContainer* container = new ShapeContainer;
Circle* circle = container->getCircle();
circle->draw();
circle->contents = 42;
circle->draw();
system("pause");
return 0;
}
这让我可以创建 Circle
s 和 Square
s,同时仍然能够访问 Circle
对象的内容!
有时您需要从全新的角度看待事物才能获得您真正想要的功能...
我有一个简单的 Shape
工厂示例,我可以在其中创建 Circle
或 Square
。
我在 Circle
class 中添加了一个额外的“contents
”属性,它不属于 Square
派生的 class 或 Shape
基础class.
问题是,当我使用我的工厂创建 Circle
class 的实例时,我无法修改创建对象的 contents
。
#include <iostream>
using namespace std;
// Shape base clas
class Shape {
public:
// Shape constructor;
Shape() {
id_ = total_++;
}
// Virtual draw method
virtual void draw() = 0;
protected:
int id_;
static int total_;
};
int Shape::total_ = 0;
// Circle derived class
class Circle : public Shape {
public:
void draw() {
contents = 0;
cout << "circle " << id_ << ": draw, contents: " << contents << endl;
}
// Attribute to attempt to access
int contents;
};
// Square derived class
class Square : public Shape {
public:
void draw() {
cout << "square " << id_ << ": draw" << endl;
}
};
// Factory class
class Factory {
public:
Shape* createCurvedInstance() {
return new Circle;
}
Shape* createStraightInstance() {
return new Square;
}
};
// Main
int main()
{
Factory* factory = new Factory;
Shape* thing = factory->createCurvedInstance();
// Draw method works fine (as it should)
thing->draw();
// Fails: "expression must have class type"
thing.contents = 4;
system("pause");
return 0;
}
当我使用工厂创建实例时,如何访问派生 class 的属性?
没办法,除非你施法,而你不得施法。多态背后的整个想法是它们通过不可变接口使自己可用的实例。他们强调了 IS-A 关系,其中 Circle 是所有意图和目的的 Shape,除了实现细节,没有人对此感兴趣。如果你公开添加 'contents' 到你的 Circle,它就不再是 Shape,所以它不应该通过工厂来建造。
由于 Shape
没有 content
,您不能将 content
从指针修改为 Shape
。句号。
但是,如果您知道您的特定 Shape
实际上是一个 Circle
并且有 content
,您可以转换为指向 Circle
的指针。
void set_content(Shape*shape, int content)
{
auto circle = dynamic_cast<Circle*>(shape);
if(circle)
circle->content = content;
}
这个版本特别安全:它不假设 shape
是 Circle*
,而是使用 dynamic_cast<Circle*>
,只有在 return 非空 shape
实际上是 Circle*
.
dynamic_cast<>
会产生一些费用,您可能希望避免这些费用。如果您有任何其他万无一失的方法来确定您的 shape
实际上是 Circle
,您可以使用简单的 static_cast<>
:
class Shape
{
public:
virtual bool has_content() const { return false; }
// ...
};
class ShapeWithContent : public Shape
{
public:
bool has_content() const override { return true; }
int content = 0;
};
class Circle : public ShapeWithContent
{
// ...
};
void set_content(Shape*shape, int content)
{
if(shape->has_content())
static_cast<ShapeWithContent*>(shape)->content = content;
}
说了这么多,我想强调的是,您应该尝试以制造此类技巧的方式设计代码 redundant/unnecessary。
原来我压根就不想建工厂!
我真正想要的是某种容器 class 来容纳不同类型的形状。这两种形状都不是从 ShapeContainer
class 派生的,但是可以通过它访问这两种形状。
#include <iostream>
using namespace std;
// Circle class
class Circle {
public:
Circle() {
contents = 2;
}
void draw() {
cout << "circle " << contents << endl;
}
int contents;
};
// Square class
class Square {
public:
void draw() {
cout << "square" << endl;
}
};
// Shape containter class
class ShapeContainer {
public:
Circle* getCircle() {
return new Circle;
}
Square* getSquare() {
return new Square;
};
};
// Main
int main()
{
ShapeContainer* container = new ShapeContainer;
Circle* circle = container->getCircle();
circle->draw();
circle->contents = 42;
circle->draw();
system("pause");
return 0;
}
这让我可以创建 Circle
s 和 Square
s,同时仍然能够访问 Circle
对象的内容!
有时您需要从全新的角度看待事物才能获得您真正想要的功能...