将对象传递给它时方法签名之间的C ++差异

时间:2014-11-08 11:20:42

标签: c++ pointers object

我看到有三种不同的方法可以将对象传递给C ++中的函数。假设我的班级是这样的:

class Test {
    int i;
public:
    Test(int x);
    int getX();
    void setX(int num);
};

Test::Test(int x) {
    i = x;
}

int Test::getX() {
    return i;
}

void Test::setX(int num) {
  i = num;
}

这三个签名可以将类Test的对象传递给方法:

void temp(Test obj) {
    obj.setX(100);
}

void perm(Test *obj) {
    obj->setX(150);
}

void noidea(Test &obj) {
    obj.setX(150);
}

事实证明,第二种和第三种方法影响调用者,我在函数内修改了相同的对象,而在第一种方法中,它突出了对象的按位副本,因此不会影响调用者(如果副本修改指针成员或资源,它可能会影响调用者)。

除此之外,签名还有其他区别吗?

有人可以解释第三个签名吗?我可以理解第二个,您可以将其称为int retVal = perm(&object),其中object属于类Test。地址由函数参数中的obj捕获,因为它是类Test的对象的地址,类型是指向类Test的对象的指针,因此使用间接来访问其成员。但是第三个签名是如何工作的,以及在调用noidea(object)时究竟传递给它的是什么,以及函数参数中obj int的确切类型是什么?

2 个答案:

答案 0 :(得分:1)

第三个调用约定在技术上与第二个类似:编译器将在场景后面生成将接受对象地址的代码。从技术上讲,引用就像一个const指针,但你不必取消引用它。

语义并不完全相似:

  • 在第三种方法中你真的必须提供一个对象作为参数,而在第二种方法中你仍然可以做一些指针算术和函数调用以确定地址
  • 在第三个中你无法传递nullptr
  • 实际上在引用中,一切都像使用其他名称的原始对象一样工作。

有关语义差异的更多信息,请查看示例here

实验演示:

如果有兴趣,这里为调用生成汇编程序代码:

; 45   :    temp(t);            ; Call by value:  
    mov eax, DWORD PTR _t$[ebp]    ; Moves the content of the object  
    push    eax                    ; Put it on stack 
    call    ?temp@@YAXVTest@@@Z    ; Call temp
    add esp, 4            
; 46   :    perm(&t);            ; Call by address: 
    lea eax, DWORD PTR _t$[ebp]    ; Moves the adress of the object 
    push    eax                    ; Put it on the stack 
    call    ?perm@@YAXPAVTest@@@Z  ; Call perm 
    add esp, 4
; 47   :    noidea(t);           ; Call by reference: 
    lea eax, DWORD PTR _t$[ebp]    ; Exactly the same code as with pointes
    push    eax
    call    ?noidea@@YAXAAVTest@@@Z ; but with noidea
    add esp, 4

perm()生成的汇编代码与noidea()完全相同(值100和150除外)。我不在这里展示,但如果你有兴趣让我知道。

关于语义差异的实验:尝试在obj++;perm()的末尾做noidea():第一个将编译(增加obj指针)而第二个不是(您无法增加对象本身,因为没有为类++定义Test运算符。

答案 1 :(得分:1)

第一个和第二个之间的第一个区别是,后两种情况避免了不必要的对象复制,除了你指出的差异。如果您的班级规模很大,这可能会有所不同。

第二个和第三个之间的差异是指针传递和传递引用之间的差异。通过指针传递意味着您正在获取传递参数的内存位置的地址,而引用只是传递参数的别名。

编辑回应评论: -

void func( int& a )
{
}

int x;
func(x);

在这个“a”中只是分配给变量“x”的内存的另一个别名(或者你可以说a将是变量x的昵称,因此对“a”的任何更改都将应用于“x” “也)。因此,对func中完成的“a”的任何修改都将间接应用于变量“x”。

以参考方式和指示方式之间的差异: -

就行为和效率而言,两者基本相同。但是,存在细微差别。

1)将指针传递给函数意味着在使用之前必须检查NULL。而C ++中没有NULL引用这样的东西,所以你可以摆脱它。

注意: - 虽然您可以间接地进行NULL引用,如: -

int * ptr = NULL;
int &a = *ptr;

但这几乎没有任何意义。

2)第二,如果你传递引用而不是在那个函数中你不能重新分配对其他对象的引用(因为引用在初始化时绑定到特定对象直到它的生命周期)但是你可以用指针来做。例如: -

指针: -

int x = 4;
int *ptr = &x;
func(ptr);

void func( int *ptr )
{
 int y = 2;
 ptr = &y;    //pointer changed to point to y.It's perfectly fine.
}

参考文献: -

int x = 4;
func(x);

void func( int& a )
{
 int y= 2;
 a = y;        //compiler would shout.This is not allowed.
}