본문 바로가기
언어 정리/C++_개념_lib

Lvalue , Rvalue , 메모리 누수

by 알 수 없는 사용자 2023. 12. 27.

C++11 이후와 이전으로 개념이 조금 바뀜

C++11 이후의 개념으로 설명합니다

 

정의 :

lvalue (locator value): 메모리 상에 지속적인 주소를 가지고 있으며, 다른 변수에 할당되거나 &* 연산자를 통해 그 위치를 참조할 수 있다.

[ 세 조건 다 맞아야 함 ]

rvalue (read value): 저장되거나 참조될 수 없고 오직 읽혀질 수만 있는 표현식의 결과, 한번만 쓰이고 다시 불릴 수 없는 

[ 좌측값이 아닌 값 ]

 

위와 같이 left right 가 아닌 메모리에 지속적인 주소가 있는가, 읽기 전용 인가의 차이다.

  - 따라서 &(L-val)는 가능하나, &(R-val)는 불가능함.

 

ex) 상수 "3"은 rvalue 임. 왜냐면 "3"은 메모리 상에 지속적인 주소를 가지지만, 저장되거나 참조될 수 없고 오직 읽을 수만(only read) 있고 값을 변경할 수 없는 객체 이기 때문이다.

 

Rvalue 참조 시키는 방법 

1

int&& Lvalue = Rvalue;

ex)

int&& c = 3;  --->>  int b = 1; int* c = &b; int& d = *c;  이런 역할을 가진다 생각하면 됨 <- 대신 변수의 메모리 할당이 아주 짧게 이뤄지고 삭제 된다.

const int& Lvalue = Rvalue; 

( int& 이지만 const로 변경하지 않는다는 보장을 하기 때문에 Rval 도 가능 )

ex)

const int& d = 4;

 

------

메모리 할당 예시

r-val 참조의 경우 0x12 byte 가 할당되는 걸 볼 수 있고
추가로 변수의 메모리 할당이 아주 짧게 이뤄지고 삭제 되서, 다음 메모리 할당값이 Rval가 할당된 메모리 이전 부터 할당되는 걸 볼 수 있다.

#include <iostream>
using namespace std;

void doSomething(int& lref) { cout << "L-value ref" << endl; }
void doSomething(int&& rref) { cout << "R-value ref" << endl; }
int getResult() { return 100; }

int main()
{
  int x = 5;
  int y = getResult();
  const int cx = 6;
  const int cy = 7;
  const int return_cy = 8;
  int &&rf = 9;
  int tmp = 10;
  int tmp2 = 11;

  cout << &(x) << endl;           // 0x7ffd868cc9f0 // 0x4
  cout << &(y) << endl;           // 0x7ffd868cc9f4 // 0x4
  cout << &(cx) << endl;          // 0x7ffd868cc9f8 // 0x4
  cout << &(cy) << endl;          // 0x7ffd868cc9fc // 0x4
  cout << &(return_cy) << endl;   // 0x7ffd868cca00 // 0x4
  cout << &(rf) << endl;          // 0x7ffd868cca0c // 0x12  // int b = 1; int* c = &b; int& d = *c; <- 주소가 3개 할당 0x4*3 = 0x12
  cout << &(tmp) << endl;         // 0x7ffd868cca04 // -0x8  // int &&rf = 9; 에서 r-val reference 는 짧게 메모리 할당후 해제 되므로 "0x7ffd868cca00" 부터 재할당함.
  cout << &(tmp2) << endl;        // 0x7ffd868cca08 // 0x4
}

 

------

 

 

#include <iostream>
#include <string>
using namespace std;

class Monitor
{
    const int bb; // 메모리 할당 전
public:
    explicit Monitor() : bb(0) { // 메모리 할당 시기 ( const int bb 를 0으로 초기화 )
        std::cout << "Monitor 생성자 호출" << std::endl;
    }
    ~Monitor() {
        std::cout << "Monitor 소멸자 호출" << std::endl;
    }
    string class_name = "Monitor_member_variable";
};

Monitor class_1() {
    Monitor* addr_m = new Monitor;  // 추가로 동적 할당된 Monitor 객체는 삭제(delete)가 이뤄지지 않았기 때문에 메모리 누수를 발생 시킬 수 있다. // 사실 new 객체 생성시 자동 해제함
    Monitor& ref_m = *addr_m;
    return ref_m; // 함수가 끝나면, 리턴값(ref_m)이 참조하는 객체의 복사본이 임시 객체로 생성되고 return되기 떄문에 이 임시 객체는 rvalue 이다.
}

int func_1() { int t=1; return t; }

int main()
{
    // Rvalue 참조 방법
    Monitor&& ins_1 = class_1();        //
    const Monitor& ins_2 = class_1();   // int& 이지만 const로 변경하지 않는다는 보장을 하기 때문에 Rval 도 가능
    cout << ins_1.class_name << endl;   // 출력값 : Monitor_member_variable
    cout << ins_2.class_name << endl;   // 출력값 : Monitor_member_variable
    // cout << &(class_1()) << endl;    // &(Rval) 불가능 , &(Lval) 가 가능
    cout << "------------------------------------" << endl;
    // 둘다 임시 객체를 생성해 그 임시 객체에 대한 참조를 가능케 해서 Rvalue 의 수명을 연장 시키는 방법이다.
    int&& a = func_1();  // Rvalue 를 주소에 할당 시키는 방법 1
    cout << a << endl;      // 출력값 : 1
    cout << &a << endl;     // 출력값 : 0x7ffcdf4e20a0
    const int& b= 3;     // Rvalue 를 주소에 할당 시키는 방법 2 but 수정이 불가능 하다
    cout << b << endl;      // 출력값 : 3
    cout << *(&b) << endl;  // 출력값 : 3

    cout << "------------------------------------" << endl;
    // Lvalue 참조 방법
    int t1 = 1;
    int& ref_a = t1;
    cout << ref_a << endl;      // 출력값 : 1
    cout << &ref_a << endl;     // 출력값 : 0x7ffcdf4e2098
    int t2 = 2;
    int* point_b = &t2;
    cout << *point_b << endl;   // 출력값 : 2
    cout << point_b << endl;    // 출력값 : 0x7ffcdf4e209c

    int&& c = 3;
    const int& d = 4;
    c = 5;
    cout<< c << endl;           // 출력값 : 5
}

 

 

 

 

 

 


 

메모리 누수

하단에 내용은 동적 할당( heap )을 했으나 delete(삭제) 시키지 않은 경우 메모리 누수가 날 수 있다.

메모리 누수를 피하려면 2가지 방법이 있다.

1. heap 말고 stack 에 저장해 객체 리턴

2. 스마트 포인터 사용

Monitor class_1() {
    Monitor* addr_m = new Monitor;
    Monitor& ref_m = *addr_m;
    return ref_m;
}

int main()
{
    Monitor&& ins_1 = class_1();
    cout << ins_1.class_name << endl;
    cout << "------------------------------------" << endl;

 

1. heap 말고 stack 에 저장해 객체 리턴

 

 

2. 스마트 포인터 사용

 

 

 

'언어 정리 > C++_개념_lib' 카테고리의 다른 글

typedef  (0) 2024.01.03
#if, 초기화리스트, const, namespace  (0) 2024.01.02
template 설명 [ fold_expression ]  (0) 2023.12.27
가변인자  (1) 2023.12.27
참조와 포인터  (0) 2023.12.26

댓글