从 C#/Java 到 C++:了解 C++示例中的参考文献?
来自 C#/Java 背景,我试图了解使用 C++ 指针和引用的最佳实践。我确信这个网站已经对此进行了令人作呕的报道,但我仍然不完全理解它。我已经阅读了一些 C++ 常见问题解答,但我需要在上下文中查看它。假设我有两个类:
class Employee
{
Employee();
~Employee();
}
class Company
{
Company();
~Company();
AddEmployee(?? employee);
?? GetEmployee();
private:
std::list<Employee??> employees_;
}
其中 AddEmployee
接受一个员工对象并将其添加到员工的私有列表中。 AddEmployee
员工参数的类型应该是什么?
AddEmployee(Employee *employee);
AddEmployee(Employee& employee);
私有 std::list 应该是什么模板类型?
另外假设公司类可以修改员工,这里 const
合适吗?如果我使用引用参数,Company 类可以转身并将员工对象传递给另一个类吗?
更新
我看到很多人建议使用值语义,但来自 C#/java,这是一个挑战。一般来说,我使用接口进行依赖注入,我不确定在这种情况下是否可以使用值语义。您可以使用值语义编写接口吗?
在我现在正在处理的代码中,几乎所有对象都在堆上分配,我的成员函数看起来像 void TakeSomeObject(SomeObject *someObject)
或 MyClass(SomeService *service)
代码>.该代码可以工作,尽管随着代码变得越来越复杂,我也担心内存泄漏。我想知道是否应该更改这些方法签名以使用引用,以及这样做的影响是什么。
另外,当我阅读有关智能指针的内容时,我想浏览一下我的代码并将大多数指针更改为共享指针,但我不清楚这是否是一件好事。在这种情况下,我是否只需更改所有方法以将共享指针作为参数?
更新2
似乎共识是使用AddEmployee(const Employee&employee)
或AddEmployee(Employee&employee)
来分配堆分配的Employees 。面对强有力的证据,我愿意改变,但我不认为自己现在正在编写值语义版本: AddEmployee(EmployeeEmployee)
UPDATE 3
我尝试过编写如下方法:
AddEmployee(const Employee& employee)
{
employees_.push_back(??employee);
}
但这似乎无法尝试替换 ??当列表定义为 std::list
。这是否意味着我必须采用指针参数而不是引用参数才能执行类似的操作?
Coming from C#/Java background and I'm trying to understand best practices for using C++ pointers and references. I'm sure this has been covered ad nauseum on this site, but I still don't fully understand it. I've read some of the C++ FAQ but I need to see it in context. Suppose I have two classes:
class Employee
{
Employee();
~Employee();
}
class Company
{
Company();
~Company();
AddEmployee(?? employee);
?? GetEmployee();
private:
std::list<Employee??> employees_;
}
Where AddEmployee
takes an employee object and adds it to the private list of employees. What should the type of the AddEmployee
employee parameter be?
AddEmployee(Employee *employee);
AddEmployee(Employee& employee);
What template type should the private std::list be?
Also assuming the company class can modify the employee is const
appropriate here? And if I use a reference parameter, can the Company class turn around and pass the employee object to another class?
UPDATE
I see a lot of people suggesting using value semantics but coming from C#/java this is challenging. Generally I'm doing dependency injection using interfaces and I'm not sure if you can even use value semantics in this case. Can you code to interfaces using value semantics?
In the code I'm working on now pretty much all objects are allocated on the heap and my member functions looks like void TakeSomeObject(SomeObject *someObject)
or MyClass(SomeService *service)
. The code works although as the code grows more complicated I am worried about memory leaks as well. Part of what I was wondering if I should change those method signatures to use references instead and what the implications of doing that are.
Also when I read about smart pointers I wanted to go through my code and change most pointers to shared_ptr but it's not clear to me if this is a good thing or not. And in that case would I just change all my methods to take shared_ptr as parameter?
UPDATE 2
It seems like the consensus is to use either AddEmployee(const Employee& employee)
or AddEmployee(Employee& employee)
for heap allocated Employees. I'm open to change in the face of strong evidence but I don't see myself writing the value semantics version right now: AddEmployee(Employee employee)
UPDATE 3
I tried writing a method like:
AddEmployee(const Employee& employee)
{
employees_.push_back(??employee);
}
However this doesn't seem to work trying to replace ?? with nothing, &, or * when the list as defined as std::list<Employee*> employees_
. Does this mean I have to take a pointer parameter rather than a reference parameter to do something like this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
该参数的类型应为
Employee const&
,即对员工的常量引用。这是为了避免将副本作为参数参数,并指定不会在函数内修改引用的对象。这反过来又允许使用临时值调用该函数。列表的类型应该是
std::list< Employee >
,而类 Company
将成为此类Employee
对象的所有者(实际上list
也会)。如果您正在研究更复杂的所有权关系,就像 C# 中常见的所有权关系一样,那么最好使用智能指针容器(或来自 Boost 的指针容器)。The parameter should be of type
Employee const&
, that is a const reference to an employee. This is to avoid a copy as the parameter argument, and to specify that the referred object won't be modified from within the function. That in turn allows the function to be called with temporary values.The type of list should be
std::list< Employee >
, and theclass Company
would be the owner of suchEmployee
objects (well actually thelist
will). If you are looking at more complex ownership relationships, like the ones one usually finds in C#, you'd be better of with a container of smart pointers (or a pointer container from Boost).前两部分(关于
AddEmployee
和GetEmployee
)的答案部分取决于复制Employee
的伤害程度。它是否可以复制?如果不是,那么你必须通过引用传递它。如果它是可复制的,你希望人们轻松复制它吗?它只是一小堆简单类型,还是包含更重的对象(字符串、数组等)?如果它相当大,那么你希望尽可能少地复制,因此
对于 AddEmployee 来说,通过引用传递是合理的,确实没有理由不将其视为 const& You'。我们必须复制它以将其放入
std::list
中(稍后会详细介绍),因此将其作为常量引用并没有真正的问题,这可以是。 const Employee &
或Employee const &
; 你可以选择,对于
GetEmployee
,你应该问自己一个问题:我希望用户用它做什么?例如,在某些情况下,您希望用户能够更改存储在std::list
中的实际Employee
对象。现在,我认为这有点可疑,或者至少值得某种形式的解释,说明为什么您需要将实际的 Employee 对象交给某人进行更改,但返回引用是相当常见的。如果这样做,您还应该提供返回
const&
的GetEmployee
的const
版本。像这样:这样,如果他们有一个
const Company
,他们可以获得一个可以查看的const Employee
,但不能修改。至于
std::list
,这非常简单:您从不存储引用的容器。 永远。您的选项是值或指针。实际上,您的选项确实应该是值或智能指针(查找这些)。由于我们在这里不处理智能指针,因此我们应该只使用值。所以我建议这样:
The answer to your first two parts (about
AddEmployee
andGetEmployee
depend in part on how much copyingEmployee
hurts. Is it copyable at all? If not, then you have to pass it by reference.If it is copyable, do you want people to copy it around easily? Is it just a small bundle of simple types, or does it contain heavier objects (strings, arrays, etc)? If it's fairly bulky, then you want to copy as little as possible, so passing by reference is reasonable.
Now for
AddEmployee
, there is really no reason not to take it as aconst&
value. You're going to have to copy it to put it in thestd::list
(more on that later), so there's no real problem with taking it as a constant reference. This can be asconst Employee &
orEmployee const &
; take your pick, they mean the same.For
GetEmployee
, you should be asking yourself a question: what do I want the user to do with it? For example, in some cases, you want the user to be able to change the actualEmployee
object stored in thestd::list
. Now granted, I would consider that slightly suspect, or at least worthy of some form of explanation of why you need to give your actualEmployee
object to someone to change, but it is fairly common to return a reference.If you do, you should also provide a
const
version ofGetEmployee
that returns aconst&
. Like so:That way, if they have a
const Company
, they can get aconst Employee
that they can look at, but not modify.As for
std::list
, that's really simple: you never store a container of references. EVER. Your options are values or pointers. Actually, your options really should be values or smart pointers (look those up). Since we're not dealing with smart pointers here, we should just use values.So I would suggest this:
我相信这是一个品味问题,你应该同时签名。
然而,对于大多数初学者来说,员工列表应该是实际实例的列表,而不是指针,并且它不能是引用,但这显然是您要实现的功能的问题,
您应该很少保留指针列表除非您知道自己在做什么,否则很可能会因不释放或释放太频繁而导致内存管理问题 - C++ 不会容忍内存分配中的错误。
通过这种方式,您可以维护内部员工副本的列表,并且该接口允许拥有指针和实例的人通过引用推送它。
我一般会避免分发指向内部数据结构的指针,因此 GetEmployee 最多应该分发一个引用(这也是一种指针,但不同)——这里您可能需要访问器——一个用于 const一种用于非常量上下文。
I believe it is a matter of taste, and that you should do both signatures.
However the list of employees should for most beginers -- but it is obviously a matter of what functionality you are trying to implement -- be a list of actual instances, not pointers, and it cannot be references
You should rarely keep a list of pointers, as you likely unless you know what you are doing, will end up in a memory management problem with not freeing or freeing too often -- C++ is not forgiving with errors in memory allocation.
This way you maintain a list of copies of employees inside, and the interface allows somebody who have a pointer as well as an instances to push it by reference.
I general I would avoid handing out pointers to internal data structure, so the GetEmployee should at best hand out a reference (which is also a kind of pointer, but different) -- here you may need to have to accessors -- one for const and one for non const contexts.
这是一个
Employee
列表:您可以有一个指向
Employee
的指针列表,如下所示,但随后您必须管理实际的
Employee< /code>s(指针指向的东西)在堆上。有时这是个好主意,但在这种情况下可能只是一种痛苦。
这:
返回一个
Employee
,一个值,一个对象,大概是列表中其中一项内容的副本。您可以拥有类似以下之一的函数:它(分别)返回到实际 Employee 的引用或指针。在这种情况下,前者是一个坏主意,因为会稍微安全一些,因为列表会打乱指针,但不会影响对象 - 但在这种情况下没有好的方法来处理堆的责任:
std::list
不承诺保证其内容安全以供引用;它可能会复制其中一个Employee
,保留它并删除原始的。后者也有同样的问题,但如果使用 std::listCompany
构造Employee
是有意义的,因此删除它们也是一种很好的形式,但是然后无论叫什么GetEmployee
可能会得到一个失效的指针。其中任何一个都可以:
第二个稍微好一点,因为它避免了不必要的复制。无论哪种方式,原件的副本都会出现在列表中。不要使用
AddEmployee(Employee * employee)
;这是完全合法的,但您只是通过向其传递无效指针来增加导致错误的机会。This is a list of
Employee
s:You could have a list of pointers to
Employee
s, like thisbut then you'd have to manage the actual
Employee
s (the things the pointers point to) on the heap. Sometimes a good idea, but in this case probably just a pain.This:
returns an
Employee
, a value, an object, presumably a copy of one of the things in the list. You could have a function like one of these:which return a reference or pointer (respectively) to an actual Employee. The former is a bad idea in this case because
std::list
does not promise to keep its contents safe for references; it might make a copy of one of theEmployee
s, keep it and delete the original. The latter has the same problem, but is marginally safer if you go withstd::list<Employee *>
, because the list will shuffle the pointers but leave the objects alone-- but in that case there's no good way to deal with responsibility for the heap: it makes sense for theCompany
to construct theEmployee
s, so it's good form for it to delete them also, but then whatever calledGetEmployee
might wind up with a defunct pointer.Either of these will do:
The second is a little better because it avoids an unnecessary copy. Either way, a copy of the original will wind up in the list. Don't use
AddEmployee(Employee * employee)
; it's perfectly legal but you're just adding opportunity to cause an error by passing it an invalid pointer.取决于你想做什么。如果你想放弃所分配对象的所有权,那么你应该使用指针。但是,不能保证指针被分配或有效,因此接纳此类员工的风险更大。
您也可能对此感到不舒服:
获取对 Employee 对象的引用更安全,因为该语言不允许您传递空引用:换句话说,该引用保证被分配并且非空。只要有意义,建议您使用引用而不是指针。
更新:我现在收到你的问题,答案很复杂:尽管对于你的具体情况,最好有指向
Employee
的指针,因为它们很可能很长-lived 对象,并且您不想将它们存储在堆栈上。因此,对于您的情况,答案是list
。但是,这里有一些问题解决了决定何时使用堆栈分配与堆分配所涉及的许多细微差别:如果您使用引用并且不复制该对象,则无法将其作为 const 传递并修改它。如果您使用指针,则会进入 const 正确性您可以在这里阅读更多相关内容: http://www.parashift.com/c++-faq-lite/const- Correctness.html
是的。
Depends on what you want to do. If you want to give up ownership of the allocated object, then you should use a pointer. However, pointers are not guaranteed to be assigned or valid, so it is more risky to take in an employee of this type.
You can be bad about it too:
Taking in a reference to an
Employee
object is safer, because the language does not allow you to pass a null reference: in other words the reference is guaranteed to be allocated and non-null. It is recommended that you use a reference instead of a pointer, whenever it makes sense to do so.Update: I get your question now and the answer is complicated: although for your specific case, it would probably be better to have pointers to
Employee
since they will most likely be long-lived objects and you don't want to store those on the stack. So for your case, the answer islist<Employee*>
. However, here are a few questions that address many of the nuances involved in deciding when to go with stack allocated vs heap allocated:If you use a reference and you don't copy the object, then you can't pass it in as
const
and modify it. If you use a pointer, then you get into const correctness and you can read more abut it here: http://www.parashift.com/c++-faq-lite/const-correctness.htmlYes.