C++ 之 值传递和引用传递

1  值传递

值传递实际上是,拷贝实参的值传递给形参,常用于“小对象” (small objects)

int fact(int val) // factorial of val
{
    int ret = 1;

    // assign ret * val to ret and decrement val
    while (val > 1)
        ret *= val--; 

    return ret;
}

调用该函数:

cout << "5! is " << fact(5) << endl;

<Effective C++> 中提及,值传递适用的“小对象”为:内置类型(built-in types),STL迭代器,函数对象类型(function object types)

2  引用传递

引用传递不涉及拷贝,传递给形参的是实参变量的引用,常用来传递“大数值” (large values)

使用引用传递,有两个优点:更高效,防切断

2.1  更高效

下面是 Person 基类,及其派生类 Student 的定义

// class Person and its subclass Student
class Person{
public:
    Person();
    virtual ~Person();
private:
    std::string name;
    std::string address;
};

class Student: public Person{
public:
    Student();
    ~Student();
private:
    std::string schoolName;
    std::string schoolAddress;
};

现有一个验证学生身份的函数,形参为值传递,则拷贝实参给形参的代价是:调用基类 Person 的构造函数一次,基类内 string 型数据成员的构造函数两次,

派生类 Student 的构造函数一次,派生类内 string 型数据成员两次,最后还会调用相应的析构函数六次,共计十二次调用,自然效率低下。

而使用引用传递,并不涉及拷贝操作,故而显著的提高了效率。

// 1) pass-by-value
bool validateStudent(Student s);

// 2) pass-by-reference-to-const
bool validateStudent(const Student& s);

2.2  防切断

下面的例子中,派生类 WindowWithScrollBars 中,重写了基类 Window 的虚函数 display

// base class Windowclass Window{
public:
    std::string name() const;        // return name of window
    virtual void display() const;    // draw window and contents
};

class WindowWithScrollBars : public Window {
public:
    virtual void display() const;
};

在 printNameAndDisplay 函数中,调用了 dispaly 函数,而形参若采用值传递方式,则会发生“切断” (slicing),即 wwsb 调用的是 Window::display()

// pass-by-value is incorrect
void printNameAndDisplay(Window w)
{
    std::cout << w.name();
    w.display();
}

// WindowWithScrollBars object will be sliced off
WindowWithScrollBars  wwsb;
printNameAndDisplay(wwsb);

因为在 printNameAndDisplay 中,并不修改传递进来的参数。因此,可使用 pass-by-const-reference 的形式,避免“切断”的发生

3  动态绑定

上面"切断"的例子,实际上涉及的是 C++ 的动态绑定机制 (dynamic binding), 而动态绑定的一个关键点就是引用传递,下面看个更形象的例子:

class Quote {
public:
    std::string isbn() const;
    virtual double net_price(std::size_t n) const;
};

// Bulk_quote inherits from Quote
class Bulk_quote : public Quote {
public:
    double net_price(std::size_t) const override;
};

在 print_total 函数中,需要调用 net_price,采用 pass-by-const-reference 形式

// calculate and print the price
double print_total(ostream &os, const Quote &item, size_t n)
{
    double ret = item.net_price(n);
    return ret;
}

实际程序中,调用 Quote::net_price 还是 Bulk_quote::net_price 取决于传递进来的参数

// basic is type Quote; bulk is type Bulk_quote
print_total(cout, basic, 20); // calls Quote::net_price
print_total(cout, bulk, 20); // calls Bulk_quote::net_price

C++ 的动态绑定机制,使得程序直到运行时,才基于引用或指针绑定的对象类型,来选择调用哪个虚函数

小结:

直接套用 <C++ Programming Language> 中的建议和 <C++ Primer> 中对动态绑定的解释

1) use pass-by-value for small objects

2) use pass-by-const-reference to pass large values that you don’t need to modify

3) dynamic binding happens when a virtual function is called through a reference (or a pointer) to a base class

参考资料:

<C++ Programming Language_4th> ch 12.2.1

<Effective C++_3rd> item 20

<C++ Primer_5th> ch 6.2,  ch 15.1

时间: 2024-10-15 19:32:18

C++ 之 值传递和引用传递的相关文章

值传递,指针传递;引用传递(c++独有)本质

要理解值传递.指针传递和引用传递的区别,主要要理解函数的实参和形参,函数的作用域(自动变量.栈),内存的布局以及指针和引用的特点.这里主要总结三种参数传递方式使用的主要场合. 值传递:只给函数提供输入值,需要复制开销,大对象很少使用值传递. 指针传递:可以改变指针指向内容的值,但是不能改变指针本身,无需复制开销.如果需要改变指针本身,可以使用二重指针或者指针引用. 引用传递:除了提供输入值外,还返回操作结果,无需复制开销. #include<stdlib.h> //值传递,函数体内变量n是参数

Java中的值传递和引用传递

解释 1.Java中有没有引用传递? 答:Java中只有按值传递,没有按引用传递! 2.当一个对象被当作参数传递到一个方法中后,在此方法中可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 答:是值传递. Java参数,不管是原始类型还是引用类型,传递的都是副本(有另外一种说法是传值,但是说传副本更好理解吧,传值通常是相对传址而言).如果参数类型是原始类型,那么传过来的就是这个参数的一个副本,也就是这个原始参数的值,这个跟之前所谈的传值是一样的,如果在函数中改变了副本

c# 值类型与引用类型 值传递与引用传递

值类型与引用类型: 值类型 :1.值类型大小固定.存储在栈上.  2.不能继承,只能实现接口 3.派生自valuetype int double char float byte bool enum struct decimal 引用类型:1.在栈上存储了一个地址实际存储在堆中,大小不固定. 2.数组.类.接口.委托 string 数组 类 接口 委托 值传递与引用传递: 值类型按值传递.值类型按引用传递.引用类型按值传递.引用类型按引用传递. 值传递:默认传递都是值传递 ,把栈中内容拷贝一份引用

JavaScript的值传递和引用传递

本文和大家分享的主要是javascript中值传递和引用传递相关内容,一起来看看吧,希望对大家学习javascript有所帮助. JavaScript有5种基本的数据类型,分别是:布尔.null.undefined.String和Number.这些基本类型在赋值的时候是通过值传递的方式.值得注意的是还有另外三种类型: Array.Function和Object,它们通过引用来传递.从底层技术上看,它们三都是对象. 基本数据类型 如果一个基本的数据类型绑定到某个变量,我们可以认为该变量包含这个基本

java中参数传递--值传递,引用传递

java中的参数传递--值传递.引用传递 参数是按值而不是按引用传递的说明 Java 应用程序有且仅有的一种参数传递机制,即按值传递. 在 Java 应用程序中永远不会传递对象,而只传递对象引用.因此是按引用传递对象.Java 应用程序按引用传递对象这一事实并不意味着 Java 应用程序按引用传递参数.参数可以是对象引用,而 Java 应用程序是按值传递对象引用的. Java 应用程序中的变量可以为以下两种类型之一:引用类型或基本类型.当作为参数传递给一个方法时,处理这两种类型的方式是相同的.两

java中值传递和引用传递

本来今天刚学习的内容,然后去其他博客看了下,发现都吵起来了,就是名字原因,有的说java有值传递和引用传递,有的说引用传递本质就是值传递,我管你杂说的,只要自己理解好,代码知道运行结果就好了. 我用自己的话,自己的理解来解释下,反正都是自己写着玩,自己看的 值传递:传递的值,这个值以后怎么改变,源值不会发生改变的. 引用传递:将对象的引用地址传递过去,如果值发生改变,那么源值也发生改变. 代码如下: 值传递: public class Test1 { public static void mai

PHP值传递和引用传递的区别

PHP值传递和引用传递的区别.什么时候传值什么时候传引用 (1)按值传递:函数范围内对值的任何改变在函数外部都会被忽略 (2)按引用传递:函数范围内对值的任何改变在函数外部也能反映出这些修改 (3)优缺点: A:按值传递时,php必须复制值.特别是对于大型的字符串和对象来说,这将会是一个代价很大的操作. B.按引用传递则不需要复制值,对于性能提高很有好处. 1 <?php 2 header('content-type:text/html;charset=utf-8'); 3 4 //探讨一下 a

对几种传递的理解:值传递,地址传递,引用传递

对几种传递的理解:值传递,地址传递,引用传递 因为会用到形参和实参的概念,区别一下,形参 即在定义函数时 int add(int a , int b)中a,b,即为形参.而当调用时int(3,4)中3,4即为实参. ①值传递 #include<stdio.h> void Exchg1(int x, int y){ int tmp; tmp=x; x=y; y=tmp; printf("x= %d , y= %d\n",x,y);}int main(){ int a=4,b=

第002弹:Java 中的值传递和引用传递

在 Java 的代码开发过程中,为了尽可能提高方法的复用性,明确方法的作用,同时防止一个方法内部过于臃肿的问题,往往会创建许多方法,那么不可避免地会涉及到参数传递的问题.通常来说,我们将 Java 中的参数传递分为两种:值传递和引用传递. 值传递:参数在进入方法时,将入参深度复制一个副本,在方法内部操作的是入参的副本,在方法执行完毕之后,外部的入参没有发生任何变化. 引用传递:在方法内部操作的是参数本身,对入参做出的修改会保留到方法的外部. 那么在 Java 中,哪些情况属于值传递,哪些情况属于

java中方法的参数传递机制(值传递还是引用传递)

看到一个java面试题: 问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?  答:是值传递.Java 编程语言只有值传递参数.当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本.指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的. 以下是从其他文章里转的,只为加深理解 public class TempTest { private void te