首页 > 解决方案 > 这是动态内存分配的好做法还是坏做法?

问题描述

我见过其他人使用它,它看起来非常聪明,但我不确定这是好还是坏的做法。它可以工作,而且我个人喜欢它的工作方式,但是在更大的程序范围内这样做真的有用吗?

他们所做的是在实际函数参数中动态分配一些数据类型,并在函数中删除它。这是一个例子:

#include <iostream>

class Foo {
private:
    int number;
public:
    Foo(int n) : number(n) { }
    int num() { return number; }
    Foo* new_num (int i) { number = i; }
};

void some_func (int thing, Foo* foo);

int main() {
    std::cout << "Enter number: ";
    int n;
    std::cin >> n;
    some_func(n, new Foo(0)); // <-- uses the 'new' operator with a function argument
    return 0;
}

// calculates difference between 'thing' and 'n'
// then puts it inside the Foo object
void some_func (int thing, Foo* foo) {
    std::cout << "Enter another number: ";
    int n;
    std::cin >> n;
    std::cout << "Difference equals " << foo->new_num(thing - n)->num() << std::endl;
    delete foo; // <-- the Foo object is deleted here
}

我知道可以在函数参数中使用运算符,但我只知道使用级别 2、4 到 15 和 17 的运算符以及赋值运算符 、? :++--一元+-!~*&,sizeof和强制转换。像这样的东西:

foo((x < 3)? 5 : 6, --y * 7);
bar(player->weapon().decr_durability().charge(0.1), &shield_layers);

所以,我实际上有两个问题。

  1. new-as-an-argument 是好的做法吗?

  2. 由于显然任何返回类型的运算符都有效,如果new有效,正在使用这些良好做法吗?

    ::, new [], throw, sizeof..., typeid, noexcept,alignof

标签: c++functionmemorydynamic-memory-allocationnew-operator

解决方案


不,这一点都不聪明。它采用了一个更简单、更通用的函数,并无缘无故地降低了它的功能,同时为难以调试的错误创建了程序的入口点。

我不清楚究竟Foo::new_num是什么意思(现在它不能编译),所以我不会直接解决你的例子,但考虑以下两个代码示例:

void bad_function(int i, F * f)
{
  f->doSomething(i);
  delete f;
}

// ...

bad_function(0, new F(1, 2, 3));

相对

void good_function(int i, F & f)
{
  f.doSomething(i);
}

// ...

good_function(0, F(1, 2, 3));

在这两种情况下,您都分配了一个新的 F 对象作为方法调用的一部分,并且一旦使用完它就会被销毁,因此使用bad_function而不是good function. 但是,您可以做很多good_function不那么容易做的事情bad_function,例如

void multi_function(const std::vector<int> & v, F & f)
{
  for(int i : v) { good_function(i, f); }
}

使用该good_function版本意味着语言本身也会阻止您做各种您不想做的事情,例如

F * f;  // never initialized
bad_function(0, f); // undefined behavior, resulting in a segfault if you're lucky

它也是更好的软件工程,因为它使人们更容易从它的签名中猜测你的函数做了什么。如果我调用一个函数,其目的涉及从控制台读取一个数字并进行算术运算,我绝对不希望它删除我传入的参数,并且在我花了半个小时找出导致一些不相关的一些模糊崩溃的原因之后我会对编写该函数的人感到愤怒。


顺便说一句,假设F::doSomething不会F以任何方式改变当前实例的值,它应该被声明const

class F
{
  void doSomething(int i) const;
  // ...
};

并且good_function还应该接受一个const论点:

void good_function(int i, const F & f);

这让任何查看签名的人都可以自信地推断出该函数不会做任何愚蠢的事情,比如弄乱f传递给函数的值,因为编译器会阻止它。这反过来又让他们更快地编写代码,因为这意味着少了一件需要担心的事情。

事实上,如果我看到一个带有bad_function's 之类签名的函数并且没有明显的原因,那么我会立即担心它会做一些我不想要的事情,我可能会在使用之前阅读该函数它。


推荐阅读