从 C#/Java 到 C++:了解 C++示例中的参考文献?

发布于 2024-12-11 19:34:10 字数 1630 浏览 0 评论 0原文

来自 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

唯憾梦倾城 2024-12-18 19:34:10

AddEmployee员工参数的类型应该是什么?

该参数的类型应为Employee const&,即对员工的常量引用。这是为了避免将副本作为参数参数,并指定不会在函数内修改引用的对象。这反过来又允许使用临时值调用该函数。

私有 std::list 应该是什么模板类型?

列表的类型应该是 std::list< Employee >,而类 Company 将成为此类 Employee 对象的所有者(实际上 list 也会)。如果您正在研究更复杂的所有权关系,就像 C# 中常见的所有权关系一样,那么最好使用智能指针容器(或来自 Boost 的指针容器)。

What should the type of the AddEmployee employee parameter be?

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.

What template type should the private std::list be?

The type of list should be std::list< Employee >, and the class Company would be the owner of such Employee objects (well actually the list 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).

山有枢 2024-12-18 19:34:10

前两部分(关于 AddEmployeeGetEmployee)的答案部分取决于复制 Employee 的伤害程度。它是否可以复制?如果不是,那么你必须通过引用传递它。

如果它是可复制的,你希望人们轻松复制它吗?它只是一小堆简单类型,还是包含更重的对象(字符串、数组等)?如果它相当大,那么你希望尽可能少地复制,因此

对于 AddEmployee 来说,通过引用传递是合理的,确实没有理由不将其视为 const& You'。我们必须复制它以将其放入 std::list 中(稍后会详细介绍),因此将其作为常量引用并没有真正的问题,这可以是 。 const Employee &Employee const &; 你可以选择,

对于GetEmployee,你应该问自己一个问题:我希望用户用它做什么?例如,在某些情况下,您希望用户能够更改存储在 std::list 中的实际 Employee 对象。现在,我认为这有点可疑,或者至少值得某种形式的解释,说明为什么您需要将实际的 Employee 对象交给某人进行更改,但返回引用是相当常见的。

如果这样做,您还应该提供返回 const&GetEmployeeconst 版本。像这样:

Employee &GetEmployee();
const Employee &GetEmployee() const;

这样,如果他们有一个 const Company,他们可以获得一个可以查看的 const Employee,但不能修改

至于std::list,这非常简单:您从不存储引用的容器。 永远。您的选项是值或指针。实际上,您的选项确实应该是值或智能指针(查找这些)。由于我们在这里不处理智能指针,因此我们应该只使用值。

所以我建议这样:

class Company
{
   Company();
   ~Company();

   AddEmployee(const Employee &employee);
   const Employee &GetEmployee() const;
   Employee &GetEmployee();

private:
   std::list<Employee> employees_;
}

The answer to your first two parts (about AddEmployee and GetEmployee depend in part on how much copying Employee 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 a const& value. You're going to have to copy it to put it in the std::list (more on that later), so there's no real problem with taking it as a constant reference. This can be as const Employee & or Employee 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 actual Employee object stored in the std::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 actual Employee object to someone to change, but it is fairly common to return a reference.

If you do, you should also provide a const version of GetEmployee that returns a const&. Like so:

Employee &GetEmployee();
const Employee &GetEmployee() const;

That way, if they have a const Company, they can get a const 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:

class Company
{
   Company();
   ~Company();

   AddEmployee(const Employee &employee);
   const Employee &GetEmployee() const;
   Employee &GetEmployee();

private:
   std::list<Employee> employees_;
}
私藏温柔 2024-12-18 19:34:10

我相信这是一个品味问题,你应该同时签名。

然而,对于大多数初学者来说,员工列表应该是实际实例的列表,而不是指针,并且它不能是引用,但这显然是您要实现的功能的问题,

您应该很少保留指针列表除非您知道自己在做什么,否则很可能会因不释放或释放太频繁而导致内存管理问题 - C++ 不会容忍内存分配中的错误。

class Company
{
   Company();
   ~Company();

   AddEmployee(const Employee& employee) { employee_.push(employee); }
   AddEmployee(const Employee* employee) { employee_.push(*employee); }
   const Employee& GetEmployee() const;
   Employee& GetEmployee();

private:
   std::list<Employee> employees_;
}

通过这种方式,您可以维护内部员工副本的列表,并且该接口允许拥有指针和实例的人通过引用推送它。

我一般会避免分发指向内部数据结构的指针,因此 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.

class Company
{
   Company();
   ~Company();

   AddEmployee(const Employee& employee) { employee_.push(employee); }
   AddEmployee(const Employee* employee) { employee_.push(*employee); }
   const Employee& GetEmployee() const;
   Employee& GetEmployee();

private:
   std::list<Employee> employees_;
}

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.

如梦初醒的夏天 2024-12-18 19:34:10

这是一个 Employee 列表:

std::list<Employee> employees_;

您可以有一个指向 Employee 的指针列表,如下所示,

std::list<Employee *> employees_;

但随后您必须管理实际的 Employee< /code>s(指针指向的东西)在堆上。有时这是个好主意,但在这种情况下可能只是一种痛苦。

这:

Employee GetEmployee();

返回一个Employee,一个值,一个对象,大概是列表中其中一项内容的副本。您可以拥有类似以下之一的函数:

Employee & GetEmployee();
Employee * GetEmployee();

它(分别)返回到实际 Employee 的引用或指针。在这种情况下,前者是一个坏主意,因为 std::list 不承诺保证其内容安全以供引用;它可能会复制其中一个Employee,保留它并删除原始的。后者也有同样的问题,但如果使用 std::list会稍微安全一些,因为列表会打乱指针,但不会影响对象 - 但在这种情况下没有好的方法来处理堆的责任:Company 构造 Employee 是有意义的,因此删除它们也是一种很好的形式,但是然后无论叫什么GetEmployee 可能会得到一个失效的指针。

其中任何一个都可以:

AddEmployee(Employee employee);
AddEmployee(Employee & employee);

第二个稍微好一点,因为它避免了不必要的复制。无论哪种方式,原件的副本都会出现在列表中。不要使用AddEmployee(Employee * employee);这是完全合法的,但您只是通过向其传递无效指针来增加导致错误的机会。

This is a list of Employees:

std::list<Employee> employees_;

You could have a list of pointers to Employees, like this

std::list<Employee *> employees_;

but then you'd have to manage the actual Employees (the things the pointers point to) on the heap. Sometimes a good idea, but in this case probably just a pain.

This:

Employee GetEmployee();

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:

Employee & GetEmployee();
Employee * GetEmployee();

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 the Employees, keep it and delete the original. The latter has the same problem, but is marginally safer if you go with std::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 the Company to construct the Employees, so it's good form for it to delete them also, but then whatever called GetEmployee might wind up with a defunct pointer.

Either of these will do:

AddEmployee(Employee employee);
AddEmployee(Employee & employee);

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.

梦里南柯 2024-12-18 19:34:10

AddEmployee员工参数的类型应该是什么?

取决于你想做什么。如果你想放弃所分配对象的所有权,那么你应该使用指针。但是,不能保证指针被分配或有效,因此接纳此类员工的风险更大。

// Example where you give up ownership
Employee* employee = new Employee();

// Call the method taking in a pointer to an employee
AddEmployee(employee);

// You allocated it, but you didn't delete it,
// so whoever owns employee now will have to delete it.
employee = NULL; 

您也可能对此感到不舒服:

// Example where you give up ownership
Employee* employee = NULL;

// Oops, you're passing in a null pointer.
AddEmployee(employee);

// Allocate an employee
employee = new Employee(); 
delete employee;

// Oops, you're passing in an deallocated object which is 
// the worst case scenario because the behavior is undefined.
AddEmployee(employee);

获取对 Employee 对象的引用更安全,因为该语言不允许您传递空引用:换句话说,该引用保证被分配并且非空。只要有意义,建议您使用引用而不是指针。

AddEmployee(Employee& employee);

私有 std::list 应该是什么模板类型?

更新:我现在收到你的问题,答案很复杂:尽管对于你的具体情况,最好有指向 Employee 的指针,因为它们很可能很长-lived 对象,并且您不想将它们存储在堆栈上。因此,对于您的情况,答案是 list。但是,这里有一些问题解决了决定何时使用堆栈分配与堆分配所涉及的许多细微差别:


还假设公司类可以修改员工,const 在这里是否合适?

如果您使用引用并且不复制该对象,则无法将其作为 const 传递并修改它。如果您使用指针,则会进入 const 正确性您可以在这里阅读更多相关内容: http://www.parashift.com/c++-faq-lite/const- Correctness.html


如果我使用引用参数,Company 类可以转身将员工对象传递给另一个类吗?

是的。

What should the type of the AddEmployee employee parameter be?

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.

// Example where you give up ownership
Employee* employee = new Employee();

// Call the method taking in a pointer to an employee
AddEmployee(employee);

// You allocated it, but you didn't delete it,
// so whoever owns employee now will have to delete it.
employee = NULL; 

You can be bad about it too:

// Example where you give up ownership
Employee* employee = NULL;

// Oops, you're passing in a null pointer.
AddEmployee(employee);

// Allocate an employee
employee = new Employee(); 
delete employee;

// Oops, you're passing in an deallocated object which is 
// the worst case scenario because the behavior is undefined.
AddEmployee(employee);

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.

AddEmployee(Employee& employee);

What template type should the private std::list be?

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 is list<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:


Also assuming the company class can modify the employee is const appropriate here?

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.html


And if I use a reference parameter, can the Company class turn around and pass the employee object to another class?

Yes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文