你会选择哪个,为什么?通过指针/参考组合传递/声明

时间:2015-03-23 20:55:30

标签: c++ pointers reference

我正在研究堆栈和堆分配之间的差异,has its own long list of disputes关于何时使用哪个。我的问题与该讨论无关;它是关于测试这两个想法的语义。

考虑以下情况,描述在函数之间传递变量的常用方法:

void passByPtr(Node* n)
{n->value = 1;}
void passByRef(Node& n)
{n.value = 2;}
void passByValue(Node n)
{n.value = 3;}
void indirectionTesting()
{
    Node* heapNode = new Node();
    Node stackNode= Node();

    // Initialize values
    heapNode->value = 0, stackNode.value = 0;
    cout << heapNode->value << ", "<< stackNode.value << endl;

    // Passed ptr by ptr (pass by reference)
    passByPtr(heapNode);
    passByPtr(&stackNode);
    cout << heapNode->value  << ", "<< stackNode.value << endl;
    // Pass by reference
    passByRef(*heapNode);
    passByRef(stackNode);
    cout << heapNode->value << ", "<< stackNode.value << endl;
    // Pass by value
    passByValue(*heapNode);
    passByValue(stackNode);
    cout << heapNode->value  << ", "<< stackNode.value << endl;

    delete heapNode; 
}
int main()
{
    indirectionTesting();
    /* Output  
       0, 0
       1, 1
       2, 2
       2, 2  */
    return 0;
}

这是我学习如何处理方法之间的变量传递的两种方式。有许多支持和反对here的论据。我读了所有这些。

但是我有一个想法......你知道,我更希望调用者能够轻松地判断一个函数是否概念传值或传递-参考。 (即(func(&amp; var))在我的拙见中,透明度补充了封装的概念。

但是有一个问题。 'new'关键字始终返回指针。从上面的例子可以看出,如果在程序中使用堆栈分配和堆分配变量的混合,很快就会混淆如何通过引用传递它们。

因此,在使用引用类型之后,现在考虑这部分代码:

void passByPtr(Node* n)
{n->value = 1;}
void passByRef(Node& n)
{n.value = 2;}
void passByValue(Node n)
{n.value = 3;}
void indirectionTesting()
{
    Node& heapNode = *new Node();
    Node stackNode= Node();

    // Initialize values
    heapNode.value = 0, stackNode.value = 0;
    cout << heapNode.value << ", "<< stackNode.value << endl;

    // Passed ptr by ptr (pass by reference)
    passByPtr(&heapNode);
    passByPtr(&stackNode);
    cout << heapNode.value << ", "<< stackNode.value << endl;
    // Pass by reference
    passByRef(heapNode);
    passByRef(stackNode);
    cout << heapNode.value << ", "<< stackNode.value << endl;
    // Pass by value
    passByValue(heapNode);
    passByValue(stackNode);
    cout << heapNode.value << ", "<< stackNode.value << endl;

    delete &heapNode;
}
int main()
{
    indirectionTesting();
    /* Output  
       0, 0
       1, 1
       2, 2
       2, 2  */ 
    return 0;
}

忽略堆分配的变量实例化看起来有多奇怪,有没有人看到这个有什么问题?

它们的功能完全相同。我看到它的方式,选择遵循这种类型的约定允许我自由选择是否在堆栈或堆上分配,同时允许我对我的所有函数使用相同的调用约定,无论它们是否是堆或者堆栈已分配。我个人认为“我会混淆哪些变量分配在堆栈上以及堆上哪些变量”的论证中有任何有效性,但如果您认为我错了,请分享您的意见。

有没有人看到这种方法有什么问题?

3 个答案:

答案 0 :(得分:0)

我建议通过引用传递函数。引用表示该对象存在。

当通过指针传递时,接收函数不知道指针是否有效,并且必须测试指针。

通过引用传递是一种更安全,更健壮的风格。

答案 1 :(得分:0)

作为最后的手段通过指针传递。

最佳选择 - 尽可能通过引用传递。引用阻止您传递空指针和悬空指针。它还可以防止你将null作为“无效输入”传递给坏的坏习惯。

还有一些你必须通过const引用传递的地方,比如在任何获取临时对象作为参数的函数中,或者复制构造函数

另外,每个重量超过sizeof(void *)的对象都可以通过引用传递它们 - 参考传递将花费更少的字节复制,因此程序更快。如果需要更改,则将重对象作为引用传递,如果不需要则将其作为const引用。

如果你不能通过引用传递,请尝试通过智能指针传递 - std :: weak_ptr和std :: shared_ptr(你不能传递std :: unique_ptr,或者至少,你不应该)。

在以下情况下通过原始指针传递:

  1. 您不能通过引用传递,例如传递this
  2. null是一个合法的论点
  3. C-API的
  4. 如果符合以下情况,则不会通过指针或参考传递:

    • 您确实希望复制您的对象
    • 没有性能提升。例如,我传递一个字符并且我不希望它被更改,但是,将它作为const引用传递会使程序变慢(引用通常在幕后实现为指针,并且它们的重量比单个字符重很多倍)

    更多的事情:

    1. CAN 通过引用捕获new结果,首先取消引用它: int& x = *new int(4)
    2. 除非您处理性能,否则不要使用new / delete。使用智能指针代替(std :: shared_ptr,std :: unique_ptr)。
    3. 即使你有一个指针作为变量(例如,如果你是imlement链表或单例并使用原始指针),你仍然可以并且应该将它作为参考返回:

      T&安培; T ::的getInstance(){    if(!m_Instance){      m_Instance = new T();     }    return * m_Instance; }

    4. 再次,上面的代码CAN AND应该与智能指针一起使用

答案 2 :(得分:0)

如果你真的想这样做:

  • 让调用者确定参数是通过ref,value还是ptr
  • 能够在堆栈和堆之间交换对象类型

然后我认为你可以沿着这些方向做点什么:

auto my_node = make_object<Node>(); // factory hides value/heap creation

然后,您将创建的每个对象都包含在具有指针语义的类中。因此,如果my_node在堆上,它被包装,所以一切都使用my_node-&gt;或* my_node(即使包装器只是将其存储为成员变量)。

由于您现在正在使用包装类型,因此函数可能具有以下签名:

value<Node> my_function( parameter<Node> );

我还没有想过这些课程如何运作的细节,但我正在思考以下几点:

参数只能从reference,value或ptr构造。所以调用者然后指定它想要哪些,并且一些机制在封面处理以正确的形式获取参数。

// in the function with the my_node object
auto ret = my_function( pass_by_reference(my_node) );

我确定在实现中会有一些棘手的问题,但在表面层面上,我认为沿着这些线条会让你重载make_object,因此不同的对象类型会根据需要与堆/堆栈相关联。

你无法使用它作为参考的任何东西 - 你必须处理指针语法,因为它是最低的公分母。但是,您可以保留某些引用或指针(可以为null或反弹)或值(在堆栈上)的语义。

显然,某些地方会出现效率低下的原因,因为通用参数&#39;类型需要能够在3种不同的模式下运行。

但是你知道 - 你正试图让语言做一些它没有被设计的东西。然而,C ++的优势在于它的意志,这是一种方式。

也许有人会告诉我为什么以上不起作用。我很高兴听到这个消息,但在粗略的考虑下,我认为可以让它发挥作用。