C++类设计 - 轻松初始化/构建对象
我使用 C++ 构建了一个具有许多 setter 函数的类,以及可能在运行时连续调用的各种函数。 所以我最终得到的代码如下所示:
A* a = new A();
a->setA();
a->setB();
a->setC();
...
a->doA();
a->doB();
不,这很糟糕,但我不喜欢输入“a->”反复。
所以我重写了我的类定义,如下所示:
class A{
public:
A();
virtual ~A();
A* setA();
A* setB();
A* setC();
A* doA();
A* doB();
// other functions
private:
// vars
};
那么我可以像这样初始化我的类:(方法1)
A* a = new A();
a->setA()->setB()->setC();
...
a->doA()->doB();
(我更喜欢它,因为它更容易编写)
要更精确地实现此功能,您可以查看我在 http:// 编写的 SDL Sprite C++ 类ken-soft.com/?p=234
一切似乎都工作得很好。不过,我对这种方法的任何反馈都很感兴趣。 我注意到一个问题。如果我像这样初始化我的类:(方法2)
A a = A();
a.setA()->setB()->setC();
...
a.doA()->doB();
然后我会遇到各种内存问题,有时事情无法正常工作(您可以通过更改我在 main.c 中初始化所有 Sprite 对象的方式来看到这一点。我的 Sprite 演示的 cpp)。
这正常吗?或者行为应该是相同的吗?
编辑设置器主要是为了让我的初始化工作更轻松。我的主要问题是方法 1 和方法 2 对我来说表现不同吗?
编辑:下面是一个 getter 和 setter 示例:
Sprite* Sprite::setSpeed(int i) {
speed = i;
return this;
}
int Sprite::getSpeed() {
return speed;
}
Using C++ I built a Class that has many setter functions, as well as various functions that may be called in a row during runtime.
So I end up with code that looks like:
A* a = new A();
a->setA();
a->setB();
a->setC();
...
a->doA();
a->doB();
Not, that this is bad, but I don't like typing "a->" over and over again.
So I rewrote my class definitions to look like:
class A{
public:
A();
virtual ~A();
A* setA();
A* setB();
A* setC();
A* doA();
A* doB();
// other functions
private:
// vars
};
So then I could init my class like: (method 1)
A* a = new A();
a->setA()->setB()->setC();
...
a->doA()->doB();
(which I prefer as it is easier to write)
To give a more precise implementation of this you can see my SDL Sprite C++ Class I wrote at http://ken-soft.com/?p=234
Everything seems to work just fine. However, I would be interested in any feedback to this approach.
I have noticed One problem. If i init My class like: (method 2)
A a = A();
a.setA()->setB()->setC();
...
a.doA()->doB();
Then I have various memory issues and sometimes things don't work as they should (You can see this by changing how i init all Sprite objects in main.cpp of my Sprite Demo).
Is that normal? Or should the behavior be the same?
Edit the setters are primarily to make my life easier in initialization. My main question is way method 1 and method 2 behave different for me?
Edit: Here's an example getter and setter:
Sprite* Sprite::setSpeed(int i) {
speed = i;
return this;
}
int Sprite::getSpeed() {
return speed;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
与您的问题无关的一个注释是,语句
A a = A();
可能没有达到您的预期。在 C++ 中,对象不是默认为 null 的引用类型,因此此语句几乎永远不会正确。您可能只想A a;
A a
创建A
的新实例,但是= A()
部分使用临时默认构造的A
调用A
的复制构造函数。如果您只执行了A a;
,它就会使用默认构造函数创建一个A
的新实例。如果您没有为类显式实现自己的复制构造函数,编译器将为您创建一个。编译器创建的复制构造函数只会复制另一个对象的数据;这意味着如果您有任何指针,它不会复制指向的数据。
因此,本质上,该行正在创建
A
的新实例,然后使用默认构造函数构造A
的另一个临时实例,然后复制临时A
code> 到新的A
,然后销毁临时A
。如果临时A
在其构造函数中获取资源并在其析构函数中取消分配它们,则可能会遇到对象尝试使用已取消分配的数据的问题,这是未定义的行为。以这段代码为例:
输出将类似于:
正如您所看到的,
a.myData
指向一个已经被释放的地址。如果您尝试使用它指向的数据,则可能会访问完全无效的数据,甚至是占据内存中位置的其他对象的数据。然后,一旦您的a
超出范围,它就会尝试再次删除数据,这会导致更多问题。One note unrelated to your question, the statement
A a = A();
probably isn't doing what you expect. In C++, objects aren't reference types that default to null, so this statement is almost never correct. You probably want justA a;
A a
creates a new instance ofA
, but the= A()
part invokesA
's copy constructor with a temporary default constructedA
. If you had done justA a;
it would have just created a new instance ofA
using the default constructor.If you don't explicitly implement your own copy constructor for a class, the compiler will create one for you. The compiler created copy constructor will just make a carbon copy of the other object's data; this means that if you have any pointers, it won't copy the data pointed to.
So, essentially, that line is creating a new instance of
A
, then constructing another temporary instance ofA
with the default constructor, then copying the temporaryA
to the newA
, then destructing the temporaryA
. If the temporaryA
is acquiring resources in it's constructor and de-allocating them in it's destructor, you could run into issues where your object is trying to use data that has already been deallocated, which is undefined behavior.Take this code for example:
The output will look something like:
As you can see,
a.myData
is pointing to an address that has already been deallocated. If you attempt to use the data it points to, you could be accessing completely invalid data, or even the data of some other object that took it's place in memory. And then once youra
goes out of scope, it will attempt to delete the data a second time, which will cause more problems.您在那里实现的称为流畅界面。我主要在脚本语言中遇到它们,但没有理由不能在 C++ 中使用。
What you have implemented there is called fluent interface. I have mostly encountered them in scripting languages, but there is no reason you can't use in C++.
如果您真的非常讨厌一个接一个地调用大量设置函数,那么您可能会喜欢下面的代码,对于大多数人来说,这对于解决“问题”来说太过分了。
此代码演示了如何创建一个集合函数,该函数可以接受任意数量、任意顺序的集合类。
If you really, really hate calling lots of set functions, one after the other, then you may enjoy the following code, For most people, this is way overkill for the 'problem' solved.
This code demonstrates how to create a set function that can accept set classes of any number in any order.
您可以考虑 ConstrOpt 范例。我第一次听说这个是在阅读 XML-RPC C/C++ lib 文档时: http://xmlrpc-c.sourceforge.net/doc/libxmlrpc++.html#constropt
基本上这个想法与您的类似,但是“ConstrOpt”范例使用您想要实例化的子类。然后使用默认选项在堆栈上实例化该子类,然后以与您相同的方式使用“引用链”设置相关参数。
然后,真实类的构造函数使用 constrOpt 类作为唯一的构造函数参数。
这不是最有效的解决方案,但可以帮助获得清晰且安全的 API 设计。
You may consider the ConstrOpt paradigm. I first heard about this when reading the XML-RPC C/C++ lib documentation here: http://xmlrpc-c.sourceforge.net/doc/libxmlrpc++.html#constropt
Basically the idea is similar to yours, but the "ConstrOpt" paradigm uses a subclass of the one you want to instantiate. This subclass is then instantiated on the stack with default options and then the relevant parameters are set with the "reference-chain" in the same way as you do.
The constructor of the real class then uses the constrOpt class as the only constructor parameter.
This is not the most efficient solution, but can help to get a clear and safe API design.