SoFunction
Updated on 2025-04-14

Detailed explanation of lvalues ​​in c/c++

Lvalue

definition:

  • A persistent object that still exists after the expression ends.
  • An expression with name and persistence is a variable that can appear on the left side of the equal sign and on the right side of the equal sign.

Rvalue

definition:

  • A temporary object that no longer exists after the expression ends.
  • Temporary, deceased value. Generally, it is an unaddressable constant, or an unnamed temporary object created during expression evaluation, transient.

One of the main differences between lvalues ​​and rvalues ​​is that lvalues ​​can be modified, while rvalues ​​cannot.

int number;
number = 1

In this code, number is an lvalue and 1 is an rvalue.

// The temporary value returned by getValue() is the right valueint value = getValue();

The essence of a reference is an alias, and the value of a variable can be changed by reference. Passing a reference when passing a parameter can avoid copying.

Lvalue Reference

Definition: Reference an object;

Basic usage:

    int number = 10;
    int& count = number;
    count++; // number = count = 11;
  • number is an lvalue, 10 is an lvalue
  • count is an lvalue reference, count reference number
void addCount(int& count)
{
    count++;
}

int number = 10;
addCount(number);

If you directly assign an "rvalue" to the "lvalue" reference, an error will be reported, for example:

int& number = 10;
Report an error:
mistake C2440	“initialization”: Can't get from“int”Convert to“int &”	

The const lvalue reference does not modify the pointing value, so it can point to the right value

For example:

std::vector Methods in push_back

// If there is no const, then use v.push_back(10) so that the type of 10 is an rvalue, and it cannot be compiled.void push_back(const value_type& _Val)

// You can pass literal values ​​directlyv.push_back(10) 
// Can pass expression resultsv.push_back(x+y)

Rvalue Reference

Definition: It is a reference that must be bound to an rvalue. It can point to an rvalue, not to an lvalue. Rvalue references in C++11 can implement "moving semantics" and obtain rvalue references through &&.

Rvalue reference binding rvalue

 int&& number = 1;

Error: Rvalue reference cannot be bound directly to lvalue

    int count = 2;
    int&& number = count;
    Report an error:
    error C2440: “initialization”: Can't get from“int”Convert to“int &&”
    message : Unable to bind lvalue to rvalue reference

Try to have the rvalue reference pointing to the left value

    int count = 2;
    // Use std::move to cast the lvalue to an rvalue, number = 2    int&& number1 = std::move(count);
    // equivalent to std::move type conversion, number = 2    int&& number2 = static_cast<int&&>(count);
	// count = number1 = number2 = 10
	number1 = 10;

Simple exercises:

void test(int & o) {std::cout << "lvalue." << std::endl;}
void test(int && temp) {std::cout << "rvalue." << std::endl;}

int main(){
    int a;
	int && b = 10; 

	test(a);
	test(std::move(a));
	test(b);
}
result:
a is a named variable,Having a fixed memory address is a typical lvalue。Output:"lvalue."
std::move(a) Put the lvalue a Convert to rvalue reference The return type is int&&。Output:"rvalue"
Although b The type is an rvalue reference(int&&)but b 本身is a named variable,Can get the address。 Output:"lvalue"

Conclusion: The rvalue reference type is only used to match the rvalue, not to represent an rvalue. Therefore, try not to declare variables of rvalue reference type, but only use it in function parameters to match rvalues.

Shallow Copy

A shallow copy only copies a pointer to an object, not the object itself, and the old and new objects still share the same piece of memory (branch).

  • A shallow copy is a bitwise copy object that creates a new object with an exact copy of the original object's attribute value.
  • If the attribute is a basic type, the value of the basic type is copied; if the attribute is a memory address (reference type), the memory address is copied, so if one of the objects changes this address, it will affect the other object.

Deep copy

Deep copy will create another object that is exactly the same. The new object does not share memory with the original object. Modifying the new object will not change to the original object. It is a "value" instead of a "reference" (not a branch)

  • Copy the first level object attribute or array element
  • Recursively copy object properties and array elements at all levels
  • Deep copy will copy all attributes and copy the dynamically allocated memory pointed to by the attributes. A deep copy occurs when an object is copied together with the object it refers to. Deep copy is slower and more expensive than shallow copy.

Write code:

#include <iostream>

class Vector {
    int num;
    int* a;
public:
    // Constructor    Vector(int n = 0) : num(n) {
        a = new int[num];
        for (int i = 0; i < num; ++i) {
            a[i] = i + 1;  // Initialize to 1, 2, 3...        }
    }

    // Destructor    ~Vector() {
        delete[] a;
    }

    void ShallowCopy(Vector& v);
    void DeepCopy(Vector& v);

    // Helper function for printing array content    void print() const {
        std::cout << "num = " << num << ", array content: ";
        for (int i = 0; i < num; ++i) {
            std::cout << a[i] << " ";
        }
        std::cout << std::endl;
    }

    // Helper function to modify the content of the array    void modify(int index, int value) {
        if (index >= 0 && index < num) {
            a[index] = value;
        }
    }
};

// shallow copy implementationvoid Vector::ShallowCopy(Vector& v) {
    this->num = ;
    this->a = ;
}

// Deep copy implementationvoid Vector::DeepCopy(Vector& v) {
    delete[] this->a;  // Release the original memory first    this->num = ;
    this->a = new int[num];
    for (int i = 0; i < num; ++i) {
        a[i] = [i];
    }
}

shallow copy test

int main() {
    // Test the shallow copy    std::cout << "=== Test shallow copy ===" << std::endl;
    {
        Vector v1(5);  // Create a vector containing 5 elements        std::cout << "Original v1: ";
        ();

        Vector v2(3);  // Create another vector        std::cout << "Original v2: ";
        ();

        (v1);  // Light copy        std::cout << "After shallow copy, v2: ";
        ();

        // Modify v1 to observe whether v2 changes        (0, 100);
        std::cout << "After modifying v1, v1: ";
        ();
        std::cout << "After modifying v1, v2: ";
        ();

        // It will crash here because both v1 and v2 try to delete the same piece of memory        std::cout << "Program will crash here due to double delete\n";
    }
    }
  Program output:
  === Test shallow copy ===
Original v1: num = 5, array content: 1 2 3 4 5
Original v2: num = 3, array content: 1 2 3
After shallow copy, v2: num = 5, array content: 1 2 3 4 5
After modifying v1, v1: num = 5, array content: 100 2 3 4 5
After modifying v1, v2: num = 5, array content: 100 2 3 4 5
Program will crash here due to double delete

Crash location:
    // Destructor    ~Vector() {
        delete[] a;
    }

Deep copy test

int main() {


    // Test deep copy    std::cout << "\n=== Test deep copy ===" << std::endl;
    {
        Vector v1(5);
        std::cout << "Original v1: ";
        ();

        Vector v2(3);
        std::cout << "Original v2: ";
        ();

        (v1);  // Deep copy        std::cout << "After deep copy, v2: ";
        ();

        // Modify v1 to observe whether v2 changes        (0, 100);
        std::cout << "After modifying v1, v1: ";
        ();
        std::cout << "After modifying v1, v2: ";
        ();

        // It won't crash here because each object manages its own memory        std::cout << "Program will exit normally\n";
    }

    return 0;
} 

=== Test deep copy ===
Original v1: num = 5, array content: 1 2 3 4 5
Original v2: num = 3, array content: 1 2 3
After deep copy, v2: num = 5, array content: 1 2 3 4 5
After modifying v1, v1: num = 5, array content: 100 2 3 4 5
After modifying v1, v2: num = 5, array content: 1 2 3 4 5
Program will exit normally

Explore rvalue reference again

Use the rvalue reference of C++11, overload the copy function, and modify the code to:

class Vector {
    int num;
    int* a;
public:
    // Constructor    Vector(int n = 0) : num(n) {
        a = new int[num];
        for (int i = 0; i < num; ++i) {
            a[i] = i + 1;  // Initialize to 1, 2, 3...        }
    }

    // Destructor    ~Vector() {
        delete[] a;
    }
    
    //Lvalue references formal parameters => Matches lvalue    void Copy(Vector& tmp) {

        if (nullptr != this->a){
            delete[] this->a;
        }
      
        this->num = ;
        this->a = ;
        
        // Prevent memory deletion during tmp destruction         = nullptr;    
    }
 
   //Rvalue reference formal parameters => Match the right value    void Copy(Vector&& tmp) {
        this->num = ;
        this->a = ;
    }
};

Call test

int main() {
    // Test lvalue reference version    Vector v1(5);        // v1: {1,2,3,4,5}
    Vector v2;
    (v1);        // Deep copy: v2 gets its own memory copy
    // Test rvalue reference version    Vector v3;
    (Vector(3)); // Mobile: Steal the resources of temporary objects directly
    return 0;
}

Summarize

The above is personal experience. I hope you can give you a reference and I hope you can support me more.