전체 글 (114)

12
12

출처
레퍼런스 씹어먹기

 

용어 정리
참조자 = 참조 = 레퍼런스

C++에서 참조는 변수나 상수를 가리키는 방법입니다.

즉, 또 다른 이름으로 똑같은 변수나 상수라고 컴파일러에게 알려주는 것입니다.

아래 예제를 보면

int a = 3; // a를 3으로 선언하고
int& another_a = a; // another_a에 a를 참조하면
another_a = 5; // another_a는 a의 별명 역할을 하게 됩니다.

std::cout << a; // a를 출력합니다.
5

another_a는 a를 참조했고
another_a에 5를 할당했습니다.

이상태에서 a를 출력해봤더니 5가 나옵니다.

즉, another_a를 변경하면 사실상 a를 변경하는 것과 같습니다.

C++에서 참조의 특징

C++에서는 참조 ( & ) 가 포인터( * ) 와는 달리
한 번 초기화되면 다른 변수를 가리킬 수 없습니다.
왜냐면 처음 참조한 변수를 끝까지 가리키기 때문에 선언 이후에
다른 변수로 변경은 절대 불가능합니다.

예제를 보면

int a = 10;
int &another_a = a; // another_a 는 이제 a 의 참조자!

std::cout << a << std::endl; // a 출력

int b = 3;
another_a = b; // b로 참조하는 것으로 변경이 불가능!
//따라서 another_a는 이제부터 a랑 똑같으니 a = b로 여겨짐

std::cout << a; // a 출력
10
3

위 상황에서

&another_a = b;

another_a에 b를 할당 하면

&a = b;

a에 b를 할당한다는 것과 똑같으니 에러가 뜹니다

참조 vs 포인터

참조는 한번 초기화 이후 가리킬 대상이 바뀔 수 없지만
포인터는 자유롭게 바뀔 수 있습니다.

포인터 예시)

int a = 10;
int *p = &a; // 포인터 p 는 a 를 가리킨다.
//이 때 포인터 p는 메모리에서 8바이트를 차지한다.

printf("%d \n", *p);

int b = 3;
p = &b; // 이제 포인터 p 는 a 를 버리고 b 를 가리킨다
//얘도 8 바이트

printf("%d \n", *p);
10
3

번외)
32비트 프로세서의 범용 레지스터의 크기는 32비트 즉 4바이트이고,
64비트 프로세서의 범용 레지스터의 크기는 64비트, 즉 8바이트이다.
그래서 위에선 int * p가 컴퓨터 프로세서의 비트따라 달라짐

또한, 포인터와는 달리 참조는 메모리 상에 공간 차지 안하도록 할 수 있습니다.
참조하는 대상으로 바꿔치기만 하면 되기 때문에 메모리 자리를 차지할 필요가 없기 때문입니다.

함수 인자로 레퍼런스 받는 예시

#include <iostream>

int change_val(int &p) {
  p = 3;
  return 0;
}

int main() {
  int number = 5;
  std::cout << number << std::endl;
  change_val(number);
  std::cout << number << std::endl;
}
5
3

change_val 함수로 인자(number)를 &p으로 담아서 참조하도록했습니다.
그리곤 p를 3으로 변경했더니 참조한 number이 3으로 변경되어 출력이 됩니다.

상수에 대한 참조자

#include <iostream>

int main() {
  int &ref = 4;

  std::cout << ref << std::endl;
}
error C2440: 'initializing' : cannot convert from 'int' to 'int &'

4는 상수입니다. 즉, [[리터럴(literal)]] 값이므로 참조가 불가능합니다.

대신 const 키워드를 이용하면 상수 참조자로 참조가 됩니다.

const int &ref = 4;

이제부턴 ref가 4로 고정이 되고 값이 변경되지 않습니다.

참조의 배열, 배열의 참조

C++에서는 배열 참조가 특이합니다.
C++은 C언어의 문법이 허용되는게 일반적이기 때문에

int arr[2] = {1,2}

이런 코드에서 배열 arr은 주소값이 생기고, 주소값이 생긴다는 말은 메모리에 존재한다는 것입니다.

원래 문법대로면 arr[2]는 *(arr + 1)이랑 똑같습니다.

그러나 우리가 원하는 참조는 특별한 경우가 아니면 메모리 상에 존재하지 않는 것이기 때문에
특별한 모양으로 배열 참조를 하게 됩니다.

#include <iostream>

int main() {
  // 배열 선언과 배열 참조
  int arr[3] = {1, 2, 3};
  int(&ref)[3] = arr;

  //각 참조 배열 요소의 값을 변경
  ref[0] = 2;
  ref[1] = 3;
  ref[2] = 1;

  std::cout << arr[0] << arr[1] << arr[2] << std::endl;
  return 0;
}

코드를 살펴보면

  1. ref 참조 배열은 괄호()로 먼저 감싸준 뒤
  2. 참조 배열의 크기를 지정해야하므로 참조할 배열 크기인 [3]을 붙여줍니다.
  3. 배열의 시작 주소인 arr을 그대로 참조 시킵니다.
이차원 배열도 똑같이 하면 됩니다.
int arr[3][2] = {1, 2, 3, 4, 5, 6};
int(&ref)[3][2] = arr;

레퍼런스 리턴하는 함수

int function() {
  int a = 1;
  return a;
}

int main() {
  int b = function();
  return 0;
}

main()에서 b에 function() 호출로 a 값(2)을 복사해준 뒤 a는 function()의 지역변수이므로 사라집니다.

a의 사라진다는 지역변수 특성이 중요합니다.

만약 아래처럼

int& function() {
  int a = 1;
  return a;
}

function()이 int& 타입을 리턴한다면 main()함수 안에 입장에선 int& function = a;와 똑같이 되고
이 function 값을 b같은 변수에 넣는다면 에러가 뜨게 됩니다.
왜냐하면 function는 a를 참조하는데 a가 지역변수라 사라지기 때문입니다.

이렇게 참조하던 대상이 사라진 레퍼런스를 Dangling reference라고 합니다.

함수를 참조자로 받기

int function() {
  int a = 1;
  return a;
}

int main() {
  int& c = function();
  return 0;
}

보다시피 int& c 를 main()에 선언하여 function()의 리턴 값을 참조하고 있습니다.
이 경우, 아까랑 똑같이 function()의 리턴값인 a가 지역변수로 사라지기에 에러가 뜨게 됩니다.

그러면 어떻게 해야 에러가 안뜰까요?
정답은 const 키워드를 사용하시는겁니다.

const int& c = function();

이렇게 선언을 하면 상수 레퍼런스가 되어 리턴값 a의 생명이 레퍼런스의 생명까지 연장되게됩니다.

'그냥 개발글 > C++' 카테고리의 다른 글

리터럴(literal)  (0) 2023.12.12
오버로딩 (Overloading)  (0) 2023.12.12
동적 할당 new, delete  (0) 2023.12.12
클래스  (0) 2023.12.12
COMMENT
 
12
12

이글은 모두의 코드 C++ 강의를 보고 정리하였습니다.

 

 

C언어에서 제공하는 메모리를 할당하고 해제하는 키워드로 각각 malloc, free가 있습니다.
C++에서도 사용이 가능하지만 언어에 맞는 할당 및 해제 키워드로 new, delete가 있습니다.

new 키워드

메모리 영역을 할당하는 키워드로 HEAP 영역(자유롭게 할당 및 해제하는 영역)에 메모리 할당하고 그 주소를 리턴해줍니다.

delete 키워드

가리키는 주소의 영역을 할당 해제 해주는 키워드입니다.

사용 예

/* new 와 delete 의 사용 */
#include <iostream>

int main() {
  int* p = new int;
  *p = 10;

  std::cout << *p << std::endl;

  delete p;
  return 0;
}

보다시피 new 키워드로 int 영역을 할당 후 주소를 $*p$ 포인터에 담아줍니다.
그리고 포인터에 값 10을 할당해줍니다.

그리고 끝에는 delete p로 할당해준 공간을 해제해줍니다.

주의)
만약 new로 할당된 공간이 아닌 변수를 delete로 해제하면 에러 메세지가 뜨게 됩니다.

new로 동적 할당 배열 크기 설정

사용 예)

int size;

std::cout << "array size : ";
std::cin >> size;

int *list = new int[size];

위의 코드는 size를 입력 받아 원하는 size 크기만큼의 int형 배열이 new 키워드로 생성 되어서 list라는 포인터에 담았습니다.

 

출처
https://modoocode.com/169

'그냥 개발글 > C++' 카테고리의 다른 글

오버로딩 (Overloading)  (0) 2023.12.12
레퍼런스 (참조)  (0) 2023.12.12
클래스  (0) 2023.12.12
객체  (0) 2023.12.12
COMMENT
 
12
12

이번에는 클래스에 대해 알아봅시다.

C++에서 클래스(class)란 객체를 정의하는 틀 또는 설계도로써 사용됩니다.


위의 사진처럼 메서드들과 변수들을 담는 객체를 정의하는 역할이 바로 클래스입니다.

클래스는 객체의 설계도로써 클래스로 만들어진 객체는 인스턴스(instance)라고 부릅니다.
따라서 객체의 변수나 메서드 또한 인스턴스 변수, 인스턴스 메서드라고 부르는 것입니다.

클래스 예시

class MyClass {
 private:
  int One;
  int Two;

 public:
  void set_One(int _One) {
    One = _One;
  }
  void set_Two(int _Two) {
    Two = _Two;
  }
};

int main() {
  MyClass myClass;
}

위의 코드에서 클래스 MyClass로 임의의 객체의 설계도를 만들었습니다.
그리고 이 MyClass라는 설계도를 바탕으로 main()함수에서 myClass라는 인스턴스 객체로 생성하였습니다.

인스턴스 변수(instance variable)

우리가 main()하수에서 myClass라고 인스턴스 객체로 생성하였을 때,
생성된 객체 내에 있는 One, Two는 인스턴스 변수로 불리게 됩니다.

인스턴스 함수(instance function)

또한 main()하수에서 myClass라고 인스턴스 객체로 생성하였을 때,
생성된 객체 내에 있는 set_One, set_Two는 인스턴스 함수로 불리게 됩니다.

멤버 변수(member variable)

객체 생성과 별개로 코드 상으로 클래스에 있는 변수를 구분 지을 때는 멤버 변수로 부릅니다.
즉, One, Two 두 개의 int형 변수를 코드에서 멤버 변수(member variable)라고 부릅니다.

멤버 함수(member function)

또한 객체 생성과 별개로 코드 상으로 클래스에 있는 함수를 구분 지을 때는 멤버 함수로 부릅니다.
즉, set_One, set_Two 두 개의 함수를 멤버 함수(member function)라고 부릅니다.

 

즉, 인스턴스로 생성된 객체 내의 함수와 변수는 인스턴스 함수, 인스턴스 변수로 부르고
클래스 내의 함수와 변수는 멤버 함수와 멤버 변수로 부릅니다.
멤버 함수, 멤버 변수도 인스턴스가 만들어져야 실재하기 때문에 구분해야합니다.

private vs public 키워드

위의 코드에서 멤버 변수나 멤버 함수 위에 각각 private와 public 키워드가 있습니다.
이것들을 '접근 지시자'라고 부릅니다.
이름처럼 외부에서 멤버들에게 접근이 가능한지 아닌지를 결정하는 것입니다.

 

위의 상황에서 private: 키워드 밑에 있는 one, two int형 변수들은 객체 내부에서 자기들만 접근가능하고,
외부에서 접근이 불가능합니다.

 

반대로 public: 키워드의 경우 마음대로 외부에서 set_One, set_Two 함수를 호출하여 객체의 내부 private: 부분의 변수의 값을 객체 스스로가 값을 변경하도록할 수 있습니다.

 

관련글
https://kimyir.tistory.com/18

출처
https://modoocode.com/172

'그냥 개발글 > C++' 카테고리의 다른 글

레퍼런스 (참조)  (0) 2023.12.12
동적 할당 new, delete  (0) 2023.12.12
객체  (0) 2023.12.12
const 키워드  (0) 2023.12.12
COMMENT
 
12
12

모두의 코드 C++ 강의를 보고 정리한 글입니다.


1세대 컴퓨터 언어 : 에니악, 손으로 진공관 상 전선 연결 배치로 소스코드 작성함
2세대 컴퓨터 언어 : 내장 메모리가 생기고 프로그래밍 가능해짐, 어셈블리어(Assembly language)가 생김 
3세대 컴퓨터 언어 : 절차 지향 언어(Procedural programming language), 파스칼 언어 이후 C언어가 생김
3.5세대 컴퓨터 언어 : 객체 지향 언어(OOP), C++, C#, java, python 등

객체란 어떠한 속성값과 행동을 가지고 있는 데이터입니다.
이 속성값이나 행동에는 변수나 자료 구조, 함수 또는 메서드 등이 해당됩니다.

이 객체를 통해 현실 세계의 것들을 표현하기 위해 추상화(abstraction)라는 과정이 필요합니다.
컴퓨터로는 세상의 100%를 나타낼 수 없기에 적절하게 처리하는 것입니다.
예를 들어 자동차에서 엔진, 바퀴, 브레이트 등으로 구성되있지만 결국에 우리는 '자동차 운전'만으로 생각하므로 함수로 추상화시킬 수 있고, 자동차 기름 양, 자동차 운전 거리 등은 변수로 추상화시킬 수 있습니다.

아래 그림은 객체를 형상화한 그림입니다.

 


정보를 가진 변수들을 둘러싸는 메서드들이 기능을 수행한다고 보시면 됩니다.
이러한 객체의 내부에 있는 변수나 함수를 인스턴스 변수, 인스턴스 메서드라고 부릅니다.

또한 그림에서 변수들을 메서드가 둘러싸는 이유는 '보호'때문입니다.

예를들어 '외부'에서 객체의 인스턴스 변수의 값을 바꾸지 못하고 인스턴스 메서드로만 바꿀 수 있는겁니다.

 

Animal animal; // 다른 객체 하나 선언
// 초기화 과정 생략

animal.food += 100;         // --> 다른 객체의 인스턴스 변수의 값을 변경 불가능
animal.increase_food(100);  // --> 다른 객체의 인스턴스 메서드 호출하여 변경 가능


이렇게 간접적으로 접근하는 것을 '캡슐화'라고합니다.
캡슐화를 하는 이유는 다양하지만 대표적으론 다른 객체의 내부를 몰라도 사용이 가능하다는 장점 때문입니다.

예를 들어

// animal 객체의 변수들을 증가시킴
animal.food += 100;
animal.weight += 10;

// animal 같은 기능을 수행하는 인스턴스 함수들
AnimalAddFood(100);
AnimalAddWeight(10);


이런식으로 animal 객체의 변수들을 처리하는 내부 로직을 몰라도 같은 기능을 수행하는 인스턴스 함수만 호출해도 똑같은 결과를 얻을 수 있습니다.

굳이 처리로직을 하나하나 알 필요없이 대상 객체의 메서드만 알아도 처리가 가능한 것입니다.



출처

https://modoocode.com/172

 

씹어먹는 C++ - <4 - 1. 이 세상은 객체로 이루어져 있다>

모두의 코드 씹어먹는 C++ - <4 - 1. 이 세상은 객체로 이루어져 있다> 작성일 : 2012-02-29 이 글은 84255 번 읽혔습니다. 이번 강좌에서는 객체 지향 프로그래밍의 도래객체란 무엇인가, 클래스란 무엇

modoocode.com

'그냥 개발글 > C++' 카테고리의 다른 글

동적 할당 new, delete  (0) 2023.12.12
클래스  (0) 2023.12.12
const 키워드  (0) 2023.12.12
연산자  (0) 2023.12.12
COMMENT