1. 포인터 변수
- &x : 변수 x의 메모리 상의 시작 주솟값을 반환
- int* px : (int*)는 정수형 변수의 시작 주솟값을 저장하는 [포인터]라는 data type
- *px: px라는 포인터 변수 선언 이후, px에 저장된 주솟값으로 jump, 참조 (px가 '가리키는' 변수로 jump, 참조)
- px도 물론 포인터'변수'이기 때문에 메모리 공간을 차지하고, 따라서 시작 주솟값이 있음 !!
#include <iostream>
using namespace std;
int main() {
int x = 10;
int *px = &x; //포인터 변수에는 주솟값이 들어감
cout << &x << " , " << x << endl; //x의 주솟값, 10 출력
cout << px << " , " << *px << endl; //x의 주솟값, 10 출력
return 0;
}
2. Pass by Pointer
- pass by value (int a = x; int b = y;) : a, b라는 지역 변수를 만들어 x, y 값을 단순히 복사하여 동작, swap함수를 써도 a와 b에만 값이 바뀌어 저장되는 거지, main공간의 xy 변수에는 값이 그대로이다.
- pass by reference (int &a = x; int &b = y;) : 변수 a, b의 주솟값이 x, y의 주솟값과 같아져 결국 동일한 메모리 공간을 가리킴, a는 x의 reference variable, 100% 동일한 별명과 이름 관계 (메모리 공간은 x, y 2칸 차지)
- pass by pointer (int *a = &x; int *b = &y;) : 포인터 변수 a, b에는 각각 x, y의 주솟값이 들어감, *a == x, *b == y, * 참조를 통해 x, y를 '가리키는' 것이다. (메모리 공간은 a, b, x, y 4칸 차지)
→ 세 개의 pass를 구분해서 사용하는 경우: 주솟값은 모든 변수, 함수, 객체.. 가 다 가지는 것이다. 만약 객체의 크기가 매우 클 때 pass by value로 값을 보낸다면 그 엄청난 크기의 객체를 다 복사해야 하기 때문에 메모리 측면에서 비효율적이다(메모리를 너무 많이 잡아먹음). 이때 pass by ref.로 보낸다.
→ pass by pointer는 함수를 또다른 함수의 입력으로 넣을 때 사용된다. (교수님 말씀에 따르면) pass by ref.를 잘 쓰고 pointer는 잘 안쓴다고 한다. 단, 간혹 함수에 전달할 인자가 주솟값인 특정한 경우에 사용한다 !
#include <iostream>
using namespace std;
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10, y = 20;
cout << x << " , " << y << endl; //10, 20
swap(&x, &y);
cout << x << " , " << y << endl; //20, 10
return 0;
}
3. 함수 포인터
- 포인터 변수는 함수의 시작 주솟값도 저장할 수 있다. (함수도 주솟값을 가지기 때문)
- [출력 타입] (*함수 포인터 변수)(입력 타입, 입력 타입, ...) 호출 스타일 잘 기억하기 !!
#include <iostream>
using namespace std;
int sum(int a, int b) { return a + b; }
int mult(int a, int b) { return a * b; }
// 함수를 입력parameter로 받는 함수
int evaluate(int(*f)(int, int), int a, int b) {
return f(a, b);
}
int main() {
// (int, int)의 입력, int의 출력을 가진 함수포인터
int(*func)(int, int); //함수의 주솟값 저장하는 함수 포인터(이름: func)
func = ∑ //sum함수의 주솟값을 포인터변수 func에 저장
cout << func(10, 20) << endl; //sum대신 func써도 무방
//변수 가리키는 포인터로 참조할 때는 *쓰는데, 함수포인터는 * 안붙이고 그냥 사용!!
func = &mult;
cout << func(10, 20) << endl;
cout << evaluate(&sum, 10, 20) << endl; //첫번째 입력 파라미타가 함수의 주솟값이므로
cout << evaluate(&mult, 10, 20) << endl;
return 0;
}
4. 벡터 basic
1. 벡터란?
- C++에서 벡터란 메모리의 블럭을 관리하는 [객체]이다.
- '값', '데이터'들의 모음 → 하나하나 보다는 여러 개를 관리함 (심지어 vector까지 vector안에 넣을 수 있음)
- ex) string(문자열)도 char(문자)들을 일렬로 나열한 것이므로, '문자들의' vector라고 볼 수 있다! (string 각각의 element가 char이어야 함)
- 블럭(block)안에 값을 순차적으로 저장한다. 이 때 저장된 값은 반드시 동일한 데이터 형(data type)을 가져야 한다! → homogeneous (homogeneity - 동종성)
- 각 element(원소)마다 index가 존재한다. (C++은 zero-based indexing! 0부터 카운트 시작) → 벡터의 각 원소들은 0~크기-1 까지의 인덱스를 가진다.
2. 벡터의 선언
#include <vector>
using namespace std;
int main() {
vector<int> v1; //vector<int> 까지가 v1의 data type
vector<int> v2(10);
vector<int> v3(10, 8);
vector<int> v4{1, 2, 3, 4};
vector<int> v5 = {1, 2, 3, 4, 5}; //v4와 동일한 표현
return 0;
}
- v1: vector<벡터에 저장할 데이터 타입> 벡터이름; → 벡터 이름과 저장할 데이터 타입만 선언
- v2: (초기화 크기); → 벡터 안에 저장할 데이터들의 개수, 사이즈까지 지정 (각 데이터들은 '0'으로 초기화 됨)
- v3: (초기화 크기, 초기화 값); → 벡터 안의 데이터 개수만큼 지정해준 값으로 초기화 됨
- v4: {값1, 값2, 값3, 값4}; → 특정한 값으로 벡터를 지정해줌, 적어준 값 만큼 크기도 정해짐
5. 벡터 메소드 (벡터도 '객체'이므로!!)
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v{ 1, 2, 3, 4, 5 };
v.push_back(6); //{1, 2, 3, 4, 5, 6}
v.pop_back(); //{1, 2, 3, 4, 5}
cout << v[3] << endl; //4
cout << v.at(2) << endl; //3
cout << v.size() << endl; //5
v.clear();
cout << v.empty() << endl; //ture
return 0;
}
- .push_back([데이터 형에 맞는 값]) : 벡터 맨 뒤에 새로운 값 추가
- .pop_back() : 벡터 맨 뒤의 값 삭제
- [인덱스] : 해당 인덱스에 위치하는 원소에 접근
- .at(인덱스) : [ ] 연산자와 동일한 기능 / [ ]는 연산자, .at( )은 함수
- .size() : 벡터의 크기=원소의 개수 반환
- .clear() : 벡터를 빈 벡터로 만ㄷ름
- .empty() : 벡터가 비어있으면 (아무 원소도 없으면) true반환, 그렇지 않으면 false반환
6. 벡터의 원소에 접근하기
for (int i = 0; i < vec.size(); i++)
cin >> vec[i]; //내가 원하는 값으로 vec 원소 바꾸기
for (int &elem : vec) //0부터 끝까지 element를 뽑아서 cin으로 새로쓰겠다
cin >> elem;
- 위 두 for문은 100% 동일한 동작을 함!
/*int &elem = vec[0]; -> body(cin >> elem)
int &elem = vec[1]; -> body(cin >> elem)
int &elem = vec[2]; -> body...(마지막 element까지)*/
for (int &elem : vec) //0부터 끝까지 element를 뽑아서 cin으로 새로 쓰겠다
cin >> elem;
/*int elem = vec[0]; -> body(cout << elem)
int elem = vec[1]; -> body(cout << elem)
int elem = vec[2]; -> body...(마지막 element까지)*/
for (int elem : vec)
cout << elem;
- cin으로 값을 키보드로부터 받을 땐 &걸어주기: 입력값을 vector안의 값으로 바꿔야 함 → &걸어줘서 elem과 각 원소 동기화
- cout으로 출력할 땐 x: vector안의 값을 안바꿔주고 출력만 하면 됨 → & 안 걸어줘도 됨
*) 1~3 example
#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;
void print(const vector<int> &vec) { //벡터 원소 출력->원소 바뀌지 않음
//size는 언제나 양수값, i에 unsigned붙여 더 명확하게
for (unsigned i = 0; i < vec.size(); i++)
cout << setw(4) << vec[i];
cout << endl;
}
int sum(const vector<int> &vec) { //벡터 원소 합->원소 바뀌지 않음
int result = 0;
for (int elem : vec)
result += elem;
return result;
}
int main() {
vector<int> vec = { 10, 20, 30, 40 };
cout << vec.size() << endl;
// cout << vec << endl; -> vector는 이렇게해도 화면에 출력되지 않음
print(vec);
//vetor의 element에 access할 때 (바꾸거나 가져올 때) index
vec[0] = 100;
vec.at(1) = 200; //[] == .at()
print(vec);
//vector의 가운데부터 추가,제거하는 메소드도 있음
vec.push_back(50); //마지막에 element 추가, append - 값을 할당
print(vec);
vec.pop_back(); //입력인자x, 마지막 element 제거
print(vec);
for (int &elem : vec)
cin >> elem;
for (int elem : vec)
cout << setw(4) << elem;
cout << endl;
print(vec);
return 0;
}
- &: 객체는 pass by ref.로 보내서 메모리 효율 증가시키기
- 함수 인자로 벡터를 보내줄 때 역시 &를 걸어서 보내주는게 훨씬 좋음! → &가 없으면 main의 벡터가 함수로 전달될 때 모든 원소의 값을 싹 다 복사하기 때문에 메모리 비효율적
- 주의: 이 때 &를 걸어주면 main의 벡터 원소가 원하지도 않을 때 바뀔 수가 있음 → const 상수라고 말해주기!
- const: 함수 안에서 입력 객체의 data가 바뀌지 않음을 보증
- 변경될 일 없는 변수는 const 걸어주는게 무조건 좋음 (추가 정보 → 읽기도 쉽고 안전한 코드)
- 함수 내에서 벡터안의 원소 값을 잠깐 바꾸고싶을 땐 const 지워주기
*)sample codes: https://github.com/halterman/CppBook-SourceCode
반응형
'코딩 > 객체지향프로그래밍' 카테고리의 다른 글
[C++] 포인터, 벡터, 배열 (3) (0) | 2020.07.30 |
---|---|
[C++] 포인터, 벡터, 배열 (2) (0) | 2020.07.29 |
[C++] String, File - 문자열과 파일 (2) (0) | 2020.07.25 |
[C++] String, File - 문자열과 파일 (1) (0) | 2020.07.23 |
[C++] 조건문과 반복문 (2) (0) | 2020.07.21 |