[C++이라고 C+ 맞지 말자!]
[진행 상황]
Class(1) - membervariable(5) 진행 중.. ++ 중간 기출 (1. 실행 결과 출력 문제) 까지 완료 → 실수 많이 하는 듯 꼼꼼히 보기
기출 문제 풀이
2022년도 기출
01. 출력 결과 작성하기
출력 결과 쓰기 (띄어쓰기, tab 등 꼼꼼히 적기)
1-1
#include <iostream> using namespace std; int main() { int i = 0, j = 0; const int SIZE = 3; int matrix[SIZE][SIZE] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; int t_arr[SIZE] = { matrix[0][0], matrix[0][1], matrix[0][2] }; int t_var; while (i < SIZE) { for (j = 0; j < SIZE; j++) { t_var = matrix[(i + 1) % SIZE][j]; matrix[(i + 1) % SIZE][j] = t_arr[j]; t_arr[j] = t_var; } i++; } //Print the result for (i = 0; i < SIZE; i++) { for (j = 0; j < SIZE; j++) { cout << matrix[i][j] << " "; } } return 0; } ---- 7 8 9 1 2 3 4 5 6
이건 직접 해보면 된다.
1-2
#include <iostream> using namespace std; int main() { int i = 0, j = 0; const int SIZE = 3; int matrix[SIZE][SIZE] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; int t_arr[SIZE] = { 0, 0, 0 }; for (i = 0; i < SIZE; i++) { for (j = 0; j < SIZE; j++) { t_arr[i] = t_arr[i] + *(*(matrix + j) + i); } } for (i = 0; i < SIZE; i++) { cout << t_arr[i] << " "; } return 0; } ---- 12 15 18
마찬가지로 직접 해보면 된다. 실수하는 일이 없도록 하자.
1-3
#include <iostream> using namespace std; int a = 0; int counter(int temp) { static int b = 0; int c = 0; a = a + temp; b = b + temp; c = c + temp; c = b + c; return c; } int main() { cout << "[a]: " << counter(1) << endl; cout << "[b]: " << a << endl; cout << "[c]: " << counter(2) << endl; cout << "[d]: " << a << endl; cout << "[e]: " << counter(3) << endl; cout << "[f]: " << a << endl; return 0; } ---- [a]: 2 [b]: 1 [c]: 5 [d]: 3 [e]: 9 [f]: 6
global 변수와 static 변수의 lifetime은 코드가 끝날 때 까지 이다. 이것만 기억하자.
1-4
#include <iostream> #include <string> using namespace std; class Vehicle { private: string id; int mileage; public: Vehicle() { mileage = 10; id = "None"; cout << "Const [1]: " << id << endl; } Vehicle(int m) : Vehicle(m, "None") { cout << "Const [2]: " << id << endl; } Vehicle(string c) : Vehicle(10, c) { // constructor 안에 constructor가 있다. cout << "Const [3]: " << id << endl; } Vehicle(int m, string c) { mileage = m; id = c; cout << "Const [4]: " << id << endl; } ~Vehicle() { cout << "Destructor: " << id << endl; } string getID(){ return id; } void setID(string c) { id = c; } int getMileage() { return mileage; } }; int main() { Vehicle car1; car1.setID("BMW"); Vehicle* car2 = new Vehicle(5000); car2->setID("GM"); Vehicle car3(3000, "TESLA"); Vehicle* v_arr[2] = { &car1, &car3}; for (int i = 0; i < 2; i++) { cout<<v_arr[i]->getID()<<"\t"<<v_arr[i]->getMileage()<<endl; } cout<<car2->getID()<<"\t"<<car2->getMileage()<<endl; delete car2; return 0; } ---- Const [1]: None Const [4]: None Const [2]: None Const [4]: TESLA BMW 10 TESLA 3000 GM 5000 Destructor: GM Destructor: TESLA Destructor: BMW
constructor안에 constructor가 들어갈 수도 있다.
destuctor의 실행 순서를 확인하고 시험 보자. (질문) 가장 최근에 사용된 것 부터 사라지는 건가?
02. 디버깅 문제 (오류 찾기)
해당 주제에 관련된 해답을 찾기
2-1 : reference and pointer
#include <iostream> using namespace std; void addAll(int input[], int size, int output) { output = 0; for (int i = 0; i < size; i++) { output = output + input[i]; } } int main() { int arr[] = { 3, 6, 9 }; int t_out = 0; addAll(arr, 3, t_out); cout << t_out << endl; return 0; } ---- 올바른 출력 18
이것은 레퍼런스나 포인터를 이용하면 된다. 이를 사용한 답은 다음과 같다.
// using reference #include <iostream> using namespace std; void addAll(int input[], int size, int& output) { output = 0; for (int i = 0; i < size; i++) { output = output + input[i]; } } int main() { int arr[] = { 3, 6, 9 }; int t_out = 0; addAll(arr, 3, t_out); cout << t_out << endl; return 0; } ---- // using pointer #include <iostream> using namespace std; void addAll(int input[], int size, int* output) { *output = 0; for (int i = 0; i < size; i++) { *output = *output + input[i]; } } int main() { int arr[] = { 3, 6, 9 }; int* t_out = new int; addAll(arr, 3, t_out); cout << *t_out << endl; delete t_out; return 0; }
또는 return 값을 넣어서 수정할 수도 있다. 단 이 방법은 위 문제의 주제와 어긋남으로 감점사유가 될 것이다.
#include <iostream> using namespace std; int addAll(int input[], int size, int output) { output = 0; for (int i = 0; i < size; i++) { output = output + input[i]; } return output; } int main() { int arr[] = { 3, 6, 9 }; int t_out = 0; t_out = addAll(arr, 3, t_out); cout << t_out << endl; return 0; }
2-2 : dynamic allocation and pointer (여기서 부터 하기)
#include <iostream> using namespace std; struct Teacher { int t_id; string t_name; }; struct Student { int s_id; Teacher* myTeacher; }; int main() { const int NUM_T = 2; const int NUM_S = 4; Teacher* t_arr; t_arr = new Teacher[NUM_T]; t_arr[0] = { 555, "David" }; t_arr[1] = { 666, "Jenny" }; Student* s_arr = new Student[NUM_S]; for (int i = 0; i < NUM_S; i++) { s_arr[i].s_id = i; s_arr[i].myTeacher = t_arr[i % 2]; } for (int i = 0; i < NUM_S; i++) { cout << s_arr[i].s_id << "\t" << s_arr[i].myTeacher->t_name << endl; } //We assume all memory deallocation are performed correctly. return 0; }
03. 프로그래밍 문제
신중하게 문제를 풀자!
3-1
#include <iostream> using namespace std; //Function name: sum //Input 1: arr[] (the array of integer to be summed) //Input 2: size (the number of elements in array to be summed) int sum(int arr[], int size) { //Question: Implement this function using "Recursion" } int main() { int nums1[] = { 1, 2, 3 }; //Sum is 6 cout << sum(nums1, sizeof(nums1)/sizeof(int)) << endl; int nums2[] = { 1, 3, 5, 7, 9 }; // Sum is 25 cout << sum(nums2, sizeof(nums2)/sizeof(int)) << endl; int nums3[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; //Sum is 45 cout << sum(nums3, sizeof(nums3) / sizeof(int)) << endl; return 0; } ---- 6 25 45
밑의 코드를 주석 부분에 넣어주면 코드가 잘 작동한다.
if (size > 0) { return (arr[size - 1] + sum(arr, size - 1)); } else { return 0; }
sum(arr, size-1) 대신 sum(arr[], size-1)나 sum(int arr[], int(size)-1)을 넣으면 제대로 작동하지 않는다. 이런거 헷갈리지 말자.
2021년도 기출
01. C++ Basic
01. Stored Program system with Von Neumann Architecture

c++ 파일(cpp)이 메모리 주소를 통해서 코드에 접근하고 control unit 접근하게 된다.
이후 ALU에 의해서 연산이 수행된다.

02. Variables Type
- int type

1byte = 8bits
각 변수가 얼마의 공간 차지하는지 보고, 가장 적합한 Type 사용하는 게 핵심이다.
- float type

밑 코드를 출력해보면 어떤 결과가 나올까?
#include <iostream> using namespace std; int main() { cout << 5.0 << '\n'; cout << 6.7f << '\n'; cout << 9876543.21 << '\n'; return 0; } >>5 >>6.7 >>9.87654e+06
첫 번째 값에서는 int를 출력하기 때문에 .0이 버려져 나온다. 마지막 값이 이상하게 출력되는 걸 확인할 수 있는데 이는 information loss 때문이다.
cout은 defalt precision of 6을 가지고 있다. 하지만 우리는 6자리까지 출력하라고 했으므로 loss가 일어난 것이다.
- char type
character 변수 type이다.
- variable size
sizeof 함수로 variable의 memory size를 알 수 있다.

여기서 출력되는 숫자는 compiler의 차이로 인해서 컴퓨터마다 다를 수 있다.
- auto
auto로도 type지정을 할 수 있다. 컴퓨터가 자동으로 type을 지정해 준다. 단 효율 문제는 잃어버릴 수 있다.

- String and Character literals
string 문자를 표현하는 방법이다. 여기에서는 auto를 사용하였다.
밑 처럼 이상한 주소 값이 출력되는 이유는 cout의 default가 아스키이기 때문이다.

03. etc…
- Variable initialization
변수 초기화는 밑과 같이 진행한다.
▪ int iNum1 = 10.5; //Copy initialization ▪ int iNum2(30.5); // Direct initialization ▪ int iNum3{ 20.5 }; // Uniform initialization : Error
Error가 생기는 이유는 int형 변수에 double형 값을 넣으려고 했기 때문이다. iNum1과 iNum2에서는 10, 30을 출력해 줄 것이다 (컴파일러마다 출력 값은 다를 수 있다). 따라서 이 문제를 방지하고 싶다면 {}를 쓰지 않으면 된다.
- Implicit type conversion
type이 mismatch 된다면 컴파일러는 implicit conversion를 진행한다. 이 때 information loss가 일어날 수도 있다.

위의 경우에서는 information loss가 일어나지 않지만, 큰→작은 으로 이동하면 information loss가 일어난다.
float f = 1.5; int i = 10; int h = f+i;
밑의 코드에서는 h = 11이 출력된다. 11.5가 출력되어야 하는데, information loss가 일어난 것이다.
- Explicit type conversion
프로그래머 의도와 다르게 conversion 되는 걸 막기 위해서 명시적으로 type conversion을 해주는 경우도 있다.
밑의 코드 처럼 () 괄호안에 자료형을 적으면 된다.
float x = 1.5; int sum = (int)x + 1; cout << sum;
- Constant
어떤 값이 추후에 바뀌지 않아야 하는 경우 사용한다.
밑 코드 처럼 작성하면 된다. 이 때 x를 수정하려고 하면 error가 생긴다.
const double x{10};
- Basic Oprtators
다음과 같은 기본 연산자들이 존재한다.



02. Control Flow
01. Standard input/output
cout, cin, <<을 활용해서 input과 output을 구현할 수 있다.
int i, j; cout << "Please enter an integer value: "; cin >> i; cout << "The value you entered is " << i; cout << " and its double is " << i * 2 << ".\n"; cout << "Please enter two integer values: "; cin >> i >> j; cout << "The values you entered are " << i << "\t" << j << endl; cout << " and their doubles are " << i * 2 << "\t" << j * 2 << ".\n"; ---- Please enter an integer value: 3 The value you entered is 3 and its double is 6. Please enter two integer values: 3 3 The values you entered are 3 3 and their doubles are 6 6.
\n, \t, ‘ ‘등 여러 문자열을 활용해서 출력 형태를 바꿀 수 있다.
02. Conditional statement
- if/else if/else
조건문이다. 파이썬과 거의 비슷한 문법을 가지고 있다.
if (condition) statement; else if (condition) // Optional statement; else if (condition) // Optional statement; else // Optional statement; ---- Enter your score : 60 60Points: Grade C
모든 statement를 {}로 묶어주는 게 좋다. 한 줄 statement는 묶어줄 필요 없지만 이걸 묶지 않으면 상당히 헷갈린다.
- Ternary operator
Ternary operator를 사용해서 if문을 더 간단하게 할 수도 있다.
Ternary operator는 밑과 같은 문법으로 이루어져 있다.
(condition) ? Statement (if true) : Statement (if false)
이를 이용하면 코드를 더 간결하게 나타낼 수 있다.
int max{ 0 }; int x{ 10 }, y{ 20 }; if (x>y) { max = x; } else { max = y; } cout << max; ---- int max{ 0 }; int x{ 10 }, y{ 20 }; max = (x > y) ? x : y; //max에 x또는 y를 대입하는 것 -> 여기서는 y가 출력될 것이다. cout << max;
- Switch
Switch-case문은 “variable이 constant x일 때 statement를 수행하라”라는 의미를 가지고 있는 명령문이다.
switch (variable or expression) { case constant1 : statements; break; case constant2 : statements; default : statements; }
다음은 switch case문과 if문의 비교이다. 그냥 if문을 쓰는게 좋을 것 같다. switch-case문에서 주의해야 할 점은 break를 꼭 넣어주어야 한다는 것이다. 이걸 안넣어주면 뒤에꺼도 계속 실행된다.
#include <iostream> // using namespace std; int main() { char grade{ 'F' }; cin >> grade; switch (grade) { case 'A': case 'a': cout << "85~100" << endl;; break; case 'B': case 'b': cout << "70~84" << endl; break; case 'C': case 'c': cout << "50~69" << endl; break; default: cout << "less than 50" << endl; } return 0; } ---- #include <iostream> // using namespace std; int main() { char grade{ 'F' }; cin >> grade; if (grade == 'A' || grade == 'a') cout << "85~100" << endl; else if (grade == 'B' || grade == 'b') cout << "70~84" << endl; else if (grade == 'C' || grade == 'c') cout << "50~69" << endl; else cout << "less than 50" << endl; return 0; }
03. Loop statement
- for loop
파이썬의 for문과 비슷한 문법을 가지고 있다. 주의해야 할 점은 loop에 활용할 변수도 정의해야 하고, 전위, 후위 연산자도 잘 고려해야 한다는 것이다.
for ( ① ; ② ; ③ ) statement; ① : initial statement – can be omitted ② : termination condition – can be omitted ③ : statement after each iteration – can be omitted
if문을 사용했을 때보다 길이가 줄어든 것을 확인할 수 있다.

break과 continue 또한 사용할 수 있다. break를 사용하면 중간에 끊긴다. continue를 사용하면 그 순서의 문자를 건너뛰고 출력시켜 준다.
// break; for (int i = 1; i < 11; i++) { if (i == 4) break; cout << i << "\t"; } 1 2 3 ---- // continue; for (int i = 1; i < 11; i++) { if (i % 2 == 0) continue; cout << i << "\t"; } 1 3 5 7 9
range-based for loop도 있다.
아래 세 코드가 모두 같은 기능을 수행한다. 참고하도록 하자.
arr = [1,3,5,7,9] for element in arr: print(element) ---- for(int i=0; i<sizeof(arr)/sizeof(int);i++){ int element = arr[i]; cout << element << " "; } ---- int arr[5] = { 1,3,5,7,9 }; for (int element : arr) { cout << element << " "; }
- While loop
while문의 문법은 다음과 같다.
while (condition) Statements
int iMenu{ 1 }; cout << "1. Coffee\n2. Juice\n3. Quit\n"; cout << "Select Menu? "; cin >> iMenu; cout << "Your choice is " << iMenu << endl; while (iMenu != 3) { cout << "Select Menu? "; cin >> iMenu; cout << "Your choice is " << iMenu << endl; }
- Do~While loop
do-while문의 문법은 다음과 같다.
do Statements while (condition);
int iMenu{ 1 }; cout << "1. Coffee\n2. Juice\n3. Quit\n"; do { cout << "Select Menu? "; cin >> iMenu; cout << "Your choice is " << iMenu << endl; } while (iMenu != 3);
위의 while문 예시 코드와 비교해보면 조건 - 수행의 순서가 바뀐 것을 확인할 수 있다. do-while문의 가장 큰 특징은 조건에 상관없이 한 번은 무조건 실행된다는 점이다.
- Goto Statement
Goto statement의 코드는 다음과 같다.
goto LABEL; LABEL:
Label 조건이 맞으면 Label로 이동하라는 의미를 가진다.
int i{0}, sum{ 0 }; Sum: // i++ 줄까지 label sum += i; i++; if (i < 11) goto Sum; // i가 11이 되기 전까지 i++를 해라 라는 의미로 해석할 수도 있어 else cout << sum << endl;
03. Function
01. Function
- 함수 사용 이유
함수를 사용하는 이유는 Readability (가독성), maintainability (유지보수성), code reuse (재사용성) 세 가지 때문이다.
- 함수 정의
함수에서는 총 4가지가 정의 되어야 한다.
return type, function name, parameters, function body
밑의 코드를 보면 (double) return type, (calArea) function name, (int radius) parameters, ({}) function body가 정의 되어 있는 걸 확인할 수 있다.
double calArea ( int radius ) { double dVal; dVal = radius * radius * 3.14; return dVal; }
또한 함수가 정의될 때는 선정의 되어야 한다.
선정의를 안하고 main함수부터 정의하게 된다면 오류가 생긴다.
밑의 코드와 같은 형태가 옳은 표현이다.
double calArea ( int radius ) { double dVal; dVal = radius * radius * 3.14; return dVal; }
만약 main 함수를 먼저 쓰고 싶다면 declaration을 통해서 미리 정의해도 된다.
declaration은 return type, function name, parameters를 이용해서 할 수 있다.
double calArea ( int radius );
마지막으로 모든 함수에는 return 값이 존재해야 한다.
만약 return 값이 없는 함수를 이용하고 싶다면 void를 이용해야 한다.
02. Overhead of function calls
함수가 호출되면 동작이 끝날 때 까지 일시적으로 stack에 할당 된다.
이 때 함수가 너무 많이 호출되면 stack과 heap이 만나면서 stack overflow가 되면서 exceptional한 결과가 출력된다.
밑 코드는 위와 같은 오류에 대해서 다루고 있다.
long calFact ( int = 0); int main() { int iVal{ 0 }; long dVal{0}; cout << "Enter the number? "; cin >> iVal; dVal = calFact( iVal ); cout << dVal << endl; cout << calFact() << endl; return 0; } long calFact ( int num ) { if (num == 0) return 1; else return num* calFact ( num-1 ); }
여기서는 두 가지 오류가 발생할 수 있다.
첫 번째 오류는 type 오류이다.
위 코드에 13을 입력하면 오류가 생긴다. 이유는 long은 -2147483648~2147483647 까지의 값을 지원하는데 13!은 그 이상의 값을 가지기 때문이다. 이 때는 undefined 된 동작을 수행하게 된다.
두 번째 오류는 recursion 오류이다.
10000을 입력하면 오류가 생긴다. 이유는 10000을 넘기면 함수가 10000번 호출 되기 때문에 stack와 heap이 만나게 되기 때문이다. 이 때는 exceptional한 결과가 출력되게 된다.
03. Macro and Inline function
- Macro function
이는 fuction의 역할을 하지만 stack의 공간을 차지하지는 않는 function이다.
preprocessor가 compile전에 이를 모두 바꿔준다.
문법은 다음과 같다.
① Macro Constant #define PI 3.14 ② Macro Function #define NAME(Parameter) Replacement
다음과 같이 사용할 수 있다.
#define Multiply(x, y) x*y int a = Multiply(3, 2);
- Inline function
Mecro funciton과 마찬가지로 stack의 공간을 차지하지 않는 function이다.
문법과 사용법은 밑의 코드를 확인하면 된다.
// function declaration inline int Multiply ( int , int = 1); int main() { cout << Multiply (10) ; cout << Multiply (10, 20) ; return 0; } // function definition int Multiply ( int iNum1, int iNum2) { return iNum1 * iNum2; } int main() { cout << 10 * 1 ; cout << 10 * 20 ; return 0; }
이 함수들을 사용하는 이유는 함수들을 code text로 치환 시켜서 stack에 function이 allocated 되는 걸 막기 위해서 이다.
마지막으로 세 형태의 함수들의 특징을 정리하면 다음과 같다.
• Function call Allocates some room in the stack More overhead than macro and inline functions • Macro functions Expanded by the C++ preprocessor The code text will be simply replaced • Inline functions Parsed by the compiler The compiler writes copy of the complied function definition • Macro and inline functions increase the size of a program, and may introduce some side effects
function call은 stack에 메모리를 저장하므로 매크로나 인라인 보다 메모리를 더 많이 소모한다. 매크로는 preprocessor에서 처리되면 메모리를 효율적으로 사용할 수 있다. 인라인은 컴파일러에 의해서 처리되지만 compiler가 복사본을 저장해두고 매번 불러오지 않기 때문에 메모리를 아낄 수 있다.
하지만 program의 size를 늘릴 수도 있고, side effect가 있을 수 있기 때문에 그런 걸 고려하면서 사용하면 좋다.
04. Local VS Global variables, Static variables
우리가 정의한 변수를 프로그램 어디서나 항상 사용할 수 있는 것은 아니다.
우리는 변수의 lifetime과 scope를 알아야 한다.
local variable의 lifetime과 scope는 {} 내에서만 적용된다.
global variable의 lifetime과 scope는 프로그램이 끝날 때 까지 적용된다.
static variable lifetime은 프로그램이 끝날 때까지 적용되고, scope는 {} 내에서만 적용된다.
• Local variables: variables defined inside the function body Lifetime: until the end of the set of curly braces(function/block { }) Scope: until the end of the set of curly braces(function/block { }) • Global variables: variables defined outside the function body Lifetime: until the end of the program Scope: until the end of the file • Static variables (keyword: static) Lifetime: until the end of the program Scope: until the end of the set of curly braces
밑의 코드 예제를 확인 해보자.
double g_count=0; // global variable void counter() { static int iCount{ 0 }; // static variable iCount++; g_count = iCount; cout << iCount << endl; } int main() { counter(); counter(); counter(); cout << g_count << endl; cout << iCount << endl; // error : iCount의 scope는 counter 함수 내이기 때문에 return 0; } ---- 1 2 3 3
counter 함수에서 iCount를 static variable로 정의해 두었다.
결과를 보면 3 / error가 출력된다.
먼저 3이 출력되는 이유는 iCount가 static variable이기 때문이다. 만약 iCount가 static variable이 아니라면 iCount 변수의 lifetime이 counter 함수 내로 제한 되기 때문에 계속 1로 초기화 될 것이다. 따라서 local로 정의해 두었다면 1이라는 값이 출력될 것이다.
두 번째로 error가 출력되는 이유는 scope 때문이다. static variabe의 범위는 {} 내로 정의 되기 때문에 그 밖에서는 정의할 수 없게 된다.
만약 main 함수에 double g_count = 1000이라는 문장을 추가하면 어떻게 될까?
이 때는 1000이 출력된다. local의 우선 순위가 global보다 높기 때문이다.
그럼 전역 변수나 static 변수는 어디에 저장되게 될까?
이 변수들은 stack과 heap이 아닌 또 다른 data라는 공간에 저장되어 reserved 되게 된다.
(위 예제에서 전위 후위 연산자가 사용되었다. 생각보다 헷갈리니 잘 봐두도록 하자)
다음 예제를 주의깊게 보자. 전년도 기출이다.
#include <iostream> using namespace std; int a = 0; int counter(int temp) { static int b = 0; int c = 0; a = a + temp; b = b + temp; c = c + temp; c = b + c; return c; } int main() { cout << "[a]: " << counter(1) << endl; cout << "[b]: " << a << endl; cout << "[c]: " << counter(2) << endl; cout << "[d]: " << a << endl; cout << "[e]: " << counter(3) << endl; cout << "[f]: " << a << endl; return 0; } ---- [a]: 2 [b]: 1 [c]: 5 [d]: 3 [e]: 9 [f]: 6
이 문제에서 temp가 계속 바뀐다. 이걸 무시하고 풀면 (문제를 제대로 안읽고 풀면) 답이 틀리니 주의하도록 하자.
05. etc..
- Header file
우리는 header file을 사용하므로써 module화를 해 management할 수 있다.
파이썬에서 import와 같다.

위의 그림과 같이 사용할 수 있다.
(여기서 int Multiply (int, int =1)이 뭘 의미하는지 헷갈렸음. 이 뒤에 int=1은 default 값 정의해 준거라고 생각하면 됨!)
- Preprocessor
preprocess는 compile전에 수행되는 과정이다.
앞에 #이 붙은 건 모두 preprocessing 된다.
#include
#define
#ifndef DEFINE
#endif
등이 preprocessing 된다.
- Namespace
namespace는 큰 프로젝트에서 name이 겹치는 걸 막아준다.
04. Array and Pointer 01
01. Basic of Computer Architecture
- Computer architecture

control unit과 memory 사이에서는 메모리 주소를 주고 받으며 data를 주고 받는다.


대부분의 경우에 우리는 메모리가 ‘어디에’ 할당되었는지 까지는 알 필요가 없다.
- Memory hierarchy
뭐.. 이런 것도 있다.. 정도로만 알아두자.


- Stack, Heap, and Address

우리가 알아야 할 가장 중요한 구조이다.
stack에는 compile time 동안 실행되는 data들이 저장된다.
heap에는 program이 run 되는 동안 실행되는 data들이 저장된다.
code and some data에는 전역 변수나 static 변수, 코드 등이 저장된다.
02. Pointer
- Pointer 기본 개념
Variable : 유저가 값은 저장한 주소에 붙이는 이름
Pointer : 값의 주소를 가지고 있는 변수
즉 Pointer도 하나의 변수라고 이해하면 좋다.
다음은 Pointer를 사용할 때 가장 많이 쓰는 두 가지 표현이다.
- Address-of (&) : 변수가 있는 주소를 확인할 때 사용한다.
- Dereference (*) : 특정 주소에 있는 값을 확인할 때 사용한다. 또는 특정 값에 접근할 때 사용한다.
밑의 코드처럼 사용할 수 있다.
int studentID = 201911999; cout << “address of studentID :” << &studentID; cout << “value at ” << & studentID << “ : “ << *(& studentID); ---- address of studentID :0000009AC9DDFBE4value at0000009AC9DDFBE4: 201911999
- Pointer 사용
선언은 앞에 별을 붙여서 할 수 있다.
data_type * variable_name;
initialization도 기존 변수처럼 하면 된다.
char* name{ 0 }; // nullptr (C++11) int iNum1 = 10; int* pNum1 = &iNum1; // 여기에 & 안 붙이면 당연히 오류 생긴다. 그럼 주소 값이 아니게 되므로
사용은 밑의 코드와 같이 한다. 중요하게 봐야 할 점은 dereferencing으로 주소에 있는 값에 접근한다는 점이다.
cout << "value:" << iNum1; cout << "its address : " << pNum1; *pNum1 = 20; cout << “Pointer Value: " << *pNum1; cout << “Pointer Value: " << iNum1;
diagram으로 보면 다음과 같다.

- Pointer의 size
cout<<sizeof(int*)<<endl;
이런식으로 입력해주면 포인터의 크기를 확인할 수 있다.
컴퓨터마다 다를 수 있지만 교수님 컴퓨터 기준 char : 1, int : 4, char* : 4, int* : 4 (byte)이다.
간단한 예시 코드이다. double pointer는 pointer의 주소를 저장할 수 있다. HW1에 세 번째 문제를 보면서 더 자세히 익히도록 하자.
#include <iostream> #include <string> using namespace std; int main(){ int a = 3; int * p_a = &a; int ** pp_a = &p_a; // double pointer float b = 20.5; float* p_b = &b; cout << "p_a" << p_a << endl; cout << "p_b" << p_b << endl; cout << "p_aa" << pp_a << endl; cout << "p_a" << *p_a << endl; cout << "p_b" << *p_b << endl; cout << "pp_a" << *pp_a << endl; return 0; } ---- p_a000000FBB99AF994 p_b000000FBB99AF9F4 p_aa000000FBB99AF9B8 p_a3 p_b20.5 pp_a000000FBB99AF994 // pointer a의 주소 값
03. Why use Pointer?
Swap 함수를 통해 Point의 중요성을 알아볼 수 있다.
Swap1은 call by value를 이용한 함수이고, Swap2는 call by reference를 이용한 함수이다.
#include <iostream> void intSwap1(int num1, int num2) { int temp{num1}; num1 = num2; num2 = temp; } // call by value void intSwap2(int* num1, int* num2) { int temp{*num1}; *num1 = *num2; *num2 = temp; } // call by reference int main(){ int iNum1{ 10 }; int iNum2{ 30 }; cout << iNum1 << " " << iNum2 << endl; intSwap1(iNum1, iNum2); cout << iNum1 << " " << iNum2 << endl; intSwap2(&iNum1, &iNum2); cout << iNum1 << " " << iNum2 << endl; return 0; }
위 코드를 실행해 보면 swap1함수에서는 값들이 swap되지 않을 것을 확인할 수 있다.
이는 swap1함수가 종료되며 stack에 있는 값들이 모두 deallocated되기 때문에 발생하는 문제이다.
이 문제는 static 변수를 사용해도 해결할 수 없을 것이다. (뭐 복잡하게 구현하면 어찌어찌 되겠지만 위 상태에서는 불가능할 것이다 → 교수님 말씀 : static의 lifetime을 고려하면 static 이용해도 swap할 수 없을 것이다)
하지만 swap2 함수에서는 값이 바뀐 것을 확인할 수 있다. 이는 Pointer를 이용하였기 때문이다.
밑의 그림과 같이 주소를 이용해서 값을 바꿔주는 call by reference 방식을 이용했기 때문에 값이 바뀌어서 출력되는 걸 확인할 수 있다.

04. Array
- 배열이란?
같은 type의 많은 data들이 들어있는 자료형이다.
파이썬의 list에서는 다른 type이 함께 있을 수 있지만 배열을 그렇게 할 수 없다. 배열에는 무조건 같은 type의 data만 들어있어야 한다.
배열의 정의는 자료형 뒤에 []만 붙여주면 된다.
data_type variable_name [ #_of_elements ];
배열의 사용과 초기화 방법은 밑과 같다.
int studentID[10]; studentID[index] = 201811999; // 0 ≦ index ≦ 9 // 왜 9인지는 조금 있다가 다룰 예정
int studentID[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int studentID[10] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int studentID[ ] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int studentID[10] = {1, }; // 중괄호가 아니면 에러 생긴다. 주의하도록 하자.
- char[] (String: not bulit-in type)
char type이 들어있는 자료형은 밑 코드와 같이 정의할 수 있다.
// “Hello World”, “John” char name[10]; name[0] = 'J'; name[1] = 'o'; name[2] = 'h'; name[3] = 'n'; cout << name << endl; //John****
하지만 위 코드를 출력해보면 John 뒤에 가비지 값이 존재한다.
이를 막으려면, name[4] = ‘\0’;이라는 인디케이터를 삽입해주어야 한다. 이 인디케이터를 end of string이라고 한다.
또는 밑의 코드처럼 입력 해주는 방법도 있다.
char name [] = “John”; // char name [4]를 출력 해보면 end of string이 나온다.
마지막으로 밑의 코드처럼 입력해 주는 방법도 있다. 이 때는 첫 번째 경우에서와 마찬가지로 end of string을 직접 작성해 주어야 한다.
음.. 근데 컴퓨터 문제로 밑 코드가 실행되는지 확인할 수가 없다. 되도록이면 밑의 형태는 사용하지 말자.
char name2 [] = {'J','o','h','n'}; cout << name2 << endl; //end of string 없기 때문에 에러 생김 cout << sizeof(name2) << endl;
이 때 {}로 묶는 것과 “가 아닌 ‘를 사용해야 하는 것을 명심하자.
이 외의 문자열을 작성할 때는 “를 사용해야 한다.
뭐.. 이건 내 컴퓨터 만의 오류일 수도 있다.
- char[] (String: not bulit-in type) : APIs for String Operation
string operation을 지원해주는 다양한 연산자들이 있다. 다음 밑의 그림과 같다.

밑의 코드처럼 활용할 수 있다.
#include <string.h> #include <iostream> using namespace std; int main(){ char str[20] = "Hello"; char str2[] = "World"; cout << strlen(str) << endl; cout << sizeof(str) << endl; strncat_s(str, str2, 4); cout << str << endl; if (strcmp(str, "HelloWorld") == 0) cout << "OK" << endl; else cout << "Fail" << endl; char str01[] = "10"; char str02[] = "20"; cout << atoi(str01) * atof(str02) << endl; return 0; } ---- 5 20 HelloWorl Fail 200
- strlen 함수는 순수 문자열의 길이를 출력해준다. hello에서 end of string을 빼면 5이므로 5가 출력된다.
- sizeof 함수는 전체 size를 알려준다. 초기 20 bytes가 할당 되었으므로 20이 출력된다.’
만약 world가 담겨있는 str2 변수에 strlen과 sizeof 함수를 적용시키면 어떻게 될까? : 이 때는 strlen : 5, sizeof : 6이 출력될 것이다. sizeof 함수는 end of string까지 함께 출력시켜 준다.
- strncat_s 함수는 두 문자열을 합쳐준다. hello와 world를 합치는데 world에서 4개의 문자만 합치라고 했으므로 helloworl이 출력된다.
- strcmp 함수는 문자열을 사전식으로 비교한다. 예를 들어 abc, abd이면 abc가 더 작다고 판단한다. 같으면, 0 str1이 크면 양의 정수, str2가 크면 음의 정수를 출력한다. 두 문자열이 다르므로 fail을 출력한다.
- atoi는 string을 int로 바꿔준다. 200이 출력된다.
- 다차원 배열
C++에서도 다차원 배열을 정의할 수 있다. 정의는 밑의 코드와 같이 할 수 있다. 중괄호를 사용해야 하는 점 잊지말자.
int studentID[ 3 ] = {1, 2, 3}; int studentID[ ] = {1, 2, 3}; int studentID[2][3] = {1, 2, 3, 4, 5, 6}; int studentID[2][3] = { {1, 2, 3}, {4, 5, 6} }; int studentID[ ][3] = { {1 }, {4, 5, 6} };
주의할 점은 두 번째 index 값은 꼭 정의 되어야 한다는 것이다. 위 코드를 보면 마지막 줄의 첫 번째 index는 정의하지 않았지만 두 번째 index는 정의해준 것을 확인할 수 있다.
다음은 예제 코드이다.
int studentID[][3] = { {1}, {4, 5, 6} }; cout << studentID[0][0] << endl; cout << studentID[0][1] << endl; cout << studentID[0][2] << endl; cout << studentID[1][0] << endl; cout << studentID[1][1] << endl; cout << studentID[1][2] << endl; cout << sizeof(studentID) << endl; cout << sizeof(studentID[0]) << endl;
마지막 출력에서는 24와 12가 각각 출력된다. 빈 공간은 0으로 initialization 된다. char에서는 값이 주어지지 않으면 아무것도 들어가지 않지만 int에서는 0으로 초기화 된다는 차이점이 있다.
05. Pointer and Array
배열의 이름은 Pointer로 사용될 수 있다.
배열의 0번째 주소와 같은 의미를 가진다.
#include <iostream> #include <string> using namespace std; int main(){ int iNum = 0; int iNums[3] = { 1, 2, 3 }; int* pNum = &iNum; int* pNums1 = &iNums[0]; int* pNums2 = iNums; // &iNums ? cout << pNums1 << endl << pNums2 << endl; cout << *pNums1 << endl << *pNums2 << endl; cout << iNums[1] << endl << pNums1[1] << endl << pNums2[1] << endl; return 0; } ---- 000000A93CBDFAB8 000000A93CBDFAB8 1 1 2 2 2
위의 코드에서 pNums1는 pNums2와 같은 값을 가진다.
특히 마지막 줄 출력을 보면 배열 이름 = 배열의 첫 번째 element 주소로도 해석 할 수 있지만 역으로 첫번째 element 주소 = 배열 이름으로 해석할 수도 있음을 확인할 수 있다.
06. Pointer Arithmetic
Pointer + Integer를 하면 어떤 결과가 나올까? 이는 “다음 칸으로 넘어가라”라는 의미로 해석할 수 있다. 따라서 다음 칸의 주소가 출력된다.
#include <iostream> #include <string> using namespace std; int main(){ int iNum[3]{ 1, 2, 3 }; int* pNum = iNum; // &iNum[0]; cout << pNum << endl; cout << pNum + 1 << endl; // 여기서 +1은 (int)4byte 만큼 점프해라 라는 뜻으로 해석할 수 있다. cout << pNum + 2 << endl; // 만약 char 이었으면 1byte 만큼 점프해라 라는 뜻을 가졌을 것이다. cout << *pNum << endl; cout << *(pNum + 1) << endl; // pNum[1] return 0; } ---- 000000AD4853FCD8 000000AD4853FCDC 000000AD4853FCE0 1 2
다차원에서도 활용할 수 있다. 만약 2차원 배열에서 점프를 한다면 row 크기만큼 점프를 할 것이다.
다차원 배열에서 주의해야 할 점은 double pointer이다.
밑의 코드를 살펴보자.
코드에서는 {{1,2,3}{4,5,6}}의 이차원 배열을 정의했다.
이후 iNums,*iNums,**iNums를 각각 출력 시켰다.
iNums : 배열의 첫번째 원소의 주소가 담겨있을 것이다. 이차원 배열에서 첫 번째 원소는 {{1,2,3}}이다. {{1,2,3}}의 첫번째 주소인 1의 주소가 iNums에 담겨있다.
*iNums : 배열의 첫 번째 원소의 첫째 원소 주소가 담겨있을 것이다. 이차원 배열에서 첫번째 원소의 첫째 원소는 {{1}}이다. 이 주소가 *iNums에 담겨있다.
**iNums : 1이 담겨있다.
그럼 삼차원 배열에서 원소에 접근하려면? : 위 논리에 따르면 triple pointer가 필요할 것이다. 그러나 교수님이 알려주시지 않아서 나도 잘 모른다.
#include <isostream> #include <string> using namespace std; int main(){ // row: 2, col : 3 int iNums[2][3] = { 1, 2, 3, 4, 5, 6 }; // 이렇게 연속적으로 정의할수도 있다. cout << iNums << endl; cout << *iNums << endl; cout << **iNums << endl; cout << **(iNums + 1) << endl; //4 cout << *((*iNums) + 1) << endl; //2 cout << *((*(iNums + 1)) + 1) << endl; //5 cout << *((*(iNums + 1)) + 1) + 1 << endl; //5+1이라서 6이 출력된 것이다. cout << "iNums[0]: " << iNums[0] << endl; cout << "iNums[1]: " << iNums[1] << endl; cout << "iNums[0][1]: " << &iNums[0][1] << endl; return 0; } ---- 000000379DEFF848 000000379DEFF848 1 4 2 5 6 iNums[0]: 000000379DEFF848 iNums[1]: 000000379DEFF854 iNums[0][1]: 000000379DEFF84C
밑의 그림과 함께 보면 도움이 된다.

위 코드는 꼭 꼼꼼히 보고 넘어가도록 하자.
05. Array and Pointer 02
01. Array and Pointer review
Array and Pointer 복습 부분이다.
밑의 코드에서는 double type을 이용하기 때문에 ‘+1 → 8bytes 뒤로 이동’이 될 것이다.
#include <iostream> #include <string> using namespace std; int main(){ double arr[3]; double* ptr; // declare pointer variable cout << "Displaying address using arrays: " << endl; // use for loop to print addresses of all array elements for (int i = 0; i < 3; ++i) { cout << "&arr[" << i << "] = " << &arr[i] << endl; } // ptr = &arr[0] ptr = arr; cout << "\nDisplaying address using pointers: " << endl; // use for loop to print addresses of all array elements // using pointer notation for (int i = 0; i < 3; ++i) { cout << "ptr + " << i << " = " << ptr + i << endl; } return 0; } ---- Displaying address using arrays: &arr[0] = 0000000CCCAFF808 &arr[1] = 0000000CCCAFF810 &arr[2] = 0000000CCCAFF818 Displaying address using pointers: ptr + 0 = 0000000CCCAFF808 ptr + 1 = 0000000CCCAFF810 ptr + 2 = 0000000CCCAFF818
밑에도 복습을 위한 예시 코드이다.
#include <iostream> #include <string> using namespace std; int main(){ double arr[5]; // Insert data using pointer notation cout << "Enter 5 numbers: "; for (int i = 0; i < 5; ++i) { // store input number in arr[i] cin >> *(arr + i); } // Display data using pointer notation cout << "Displaying data: " << endl; for (int i = 0; i < 5; ++i) { // display value of arr[i] cout << *(arr + i) << endl; } return 0; } ---- Enter 5 numbers: 4 1 2 3 4 Displaying data: 4 1 2 3 4
02. Passing Array to Function
배열 parameter로 전달할 때 전달 받는 함수는 배열의 크기를 알 수 없다.
우리는 배열의 주소만을 인자로 전달해주기 때문이다.
이 때 발생하는 문제가 있다. 밑의 코드를 통해 살펴보자.
(주의 사항 : 밑 코드에서 함수의 parameter 형식으로 int arr[]와 int *arr를 사용했다. 그럼 배열을 정의/초기화 할 때도 int arr[]= {1,2,3,4} 말고, int arr* = {1,2,3,4}로 정의할 수 있지 않을까 라고 생각할 수 있다. 하지만 이는 불가능하다. 정의를 저렇게 한다면 포인터인지 배열인지 구분할 수 없게 되기 때문이다)
#include <iostream> #include <string> using namespace std; void fun(int arr[])// SAME AS void fun(int *arr) { unsigned int n = sizeof(arr) / sizeof(arr[0]); cout << "Array size inside fun() is :" << n << endl; } int main() { int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; unsigned int n = sizeof(arr) / sizeof(arr[0]); // 어차피 elements의 type 모두 같으니까 [0]으로 적는듯 하다. cout << "Array size inside main() is :" << n << endl; cout << "sizeof(array) in main() is :" << sizeof(arr) << endl; fun(arr); return 0; } ---- Array size inside main() is :8 sizeof(array) in main() is :32 Array size inside fun() is :2
위 코드에서 세 번째 출력 결과가 이상함을 확인할 수 있다.
이는 우리가 함수의 size를 함께 전달하지 않아서 그렇다.
arr가 인자로 넘어가는 과정을 생각해 보아야 한다. 우리는 parameter로 arr의 시작 주소를 전달해 주었다. 함수는 이 시작 주소를 바탕으로 다음 주소를 알아내어 동작을 할 것이다. 즉 우리는 배열을 전달하지 않고 첫 주소 만을 전달한 것이다. 따라서 위 코드의 fun 함수는 배열의 size를 알 수 없고, 그렇기 때문에 exceptional한 결과를 출력시킨다.
따라서 우리는 배열을 parameter로 전달할 때 배열의 size도 함께 전달해 주어야 한다.
밑 코드는 수정한 결과이다.
#include <iostream> #include <string> using namespace std; void fun(int arr[], unsigned int n)// SAME AS void fun(int *arr) { unsigned int k = n; cout << "Array size inside fun() is :" << k << endl; } int main() { int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; unsigned int n = sizeof(arr) / sizeof(arr[0]); // 어차피 elements의 type 모두 같으니까 [0]으로 적는듯 하다. cout << "Array size inside main() is :" << n << endl; cout << "sizeof(array) in main() is :" << sizeof(arr) << endl; fun(arr, n); return 0; } ---- Array size inside main() is :8 sizeof(array) in main() is :32 Array size inside fun() is :8
밑의 코드에서도 size를 주지 않으면 이상한 결과가 출력되었을 것이다. 하지만 size를 인자로 함께 넘겨주었기 때문에 expectation한 결과가 나온다.
#include <iostream> #include <string> using namespace std; void fun2(int arr[], unsigned int n) // SAME AS void fun2(int *arr, int n) { int i; for (i = 0; i < n; i++) cout << arr[i] <<endl; } int main() { int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; unsigned int n = sizeof(arr) / sizeof(arr[0]); cout << "Array size inside main() is :" << n << endl; fun2(arr, n); return 0; } ---- Array size inside main() is :8 1 2 3 4 5 6 7 8
03. Dynamic Memory Allocation
- Dynamic allocation
동적 할당을 통해 run-time 동안 heap의 memory를 우리 마음대로 조정할 수 있다.

위 구조에 대해 한 번 더 설명하자면
stack : 여기서는 할당이 compiler에 의해서 이루어진다. return 될 때마다 초기화된다. 이 영역은 compiler에 의해서 조정이 된다.
heap : run-time 동안 살아있다. 이 영역은 user에 의해서 조정이 된다. user는 직접 allocate, deallocate 하며 memory를 사용한다.
code and some data : code와 data들이 위치하고 있는 공간이다.
동적할당은 heap에서 이루어진다. 따라서 주의해야 할 점이 있다.
int x = 10; int* ptr = &x; delete ptr; return 0;
위의 코드에서는 run-time오류가 생기는데 이유는 stack과 heap이 함께 쓰였기 때문이다. 할당은 stack에서 했는데 deallocate는 heap에서 하려고 하니까 오류가 생기는 것이다.
- Syntax
문법은 다음과 같다.
C's syntax #include <stdlib.h> void* malloc(size_t size); // Memory allocation void free(void * ptr); // Memory deallocation C++'s syntax data_type * ptr; ptr = new data_type; // Memory allocation delete ptr; // Memory deallocation
예제 코드는 다음과 같다.
C's syntax #include <stdio.h> #include <stdlib.h> int* ptr; ptr = (int*)malloc(sizeof(int)); *ptr = 123; // cout << “value of *prt : “ << *ptr; printf("value of * ptr : %d", *ptr); free(ptr); C++'s syntax int* ptr; //ptr = (int*)malloc(sizeof(int)); ptr = new int; *ptr = 123; cout << "value of * prt : " << *ptr; delete ptr;
일차원 배열에서의 쓰임은 밑의 코드와 같다.
#include <iostream> using namespace std; int main() { int* ptr; ptr = new int[10]; // ptr = new int[10] {0}; // 0 initializing ptr[0] = 0; *(ptr + 1) = 1; for (int i = 0; i < 10; i++) *(ptr + i) = i; for (int i = 0; i < 10; i++) cout << ptr[i] << " "; // delete ptr; delete[] ptr; return 0; } ---- 0 1 2 3 4 5 6 7 8 9
배열에서의 동적할당은 매우 중요하다. 동적할당을 이용하면 배열의 크기를 파라미터로 지정할 수 있다.
밑의 예제 코드를 살펴보자.
char** special_filter(const char* sentences[], int filter_offset, int num_sentences) int x; int y; // result에 값 추가하기 위한 pointer const char shap = 35; char** result = new char* [num_sentences]; // array에 변수 넣기 위해 동적할당
기존 배열에서는 배열의 크기를 파라미터로 지정하지 못한다 (배열의 크기를 지정할 때 문자를 넣으면 오류가 생기는 것을 확인할 수 있다). 동적할당은 이를 가능하게 한다. 중요한 특징이니 알아두자.
(추가 : 배열의 크기를 문자로 정하고 싶으면 const를 이용하면 된다. int a; int b[a];는 오류가 생기지만 const int a; int b[a];는 오류가 생기지 않는다)
HW1-3에서 이에 대한 내용을 다루고 있으니 살펴보도록 하자.
- Memory Leak and Dangling Pointer

먼저 Memory leak은 “동적할당한 메모리를 해제할 수 없게 된 상태”를 의미한다. 위에 그림에서 ptr 포인터에 ptr2의 주소를 할당시켰다. 이렇게 되면 ptr은 ptr2와 같이 20이란 값을 가르키게 되고, ptr 포인터는 기존의 30이란 값과 연결이 끊기게 된다. 따라서 우리는 ptr에 할당 되어있었던 30이라는 값을 해제할 수 없게 된다. Memory leak은 이렇게 기존 할당을 해제할 수 없게 되는 상황을 의미한다.
다음으로 Danging pointer는 “조급한 해제”를 의미한다. 위 그림에서 우리는 ptr3 포인터를 이용하려고 한다. 하지만 그 위에서 deallocation을 너무 성급하게 해주었기 때문에 오류가 생겨버리고 만다. 이렇게 아직 사용이 끝나지 않았는데 성급하게 공간 해제를 하는 것을 Dangling pointer라고 한다.
- Importance of Deallocation
deallocation은 중요한 과정이다. 만약 이를 수행하지 않는다면 heap과 stack만나 오류가 생길 수도 있을 것이다.
#include <iostream> using namespace std; int main() { int* ptr; while (1) { cout << "Hello"; ptr = new int[10000]; // delete[] ptr; 넣어주면 문제 해결! } return 0; }
배열에서의 deallocation은 다음과 같이 진행한다.
for (int i = 0; i < 3; i++) { delete[] p_arr[i]; } delete[] p_arr;
for문으로 배열의 모든 원소를 지우고 난 후에 배열을 지워야 한다.
04. Reference Type
reference type은 ‘별명’이라고 생각하면 좋다. 즉 이미 있는 값에 대해서 별명을 붙여주는 것이다.
- Syntax
문법은 밑의 그림과 같다.

위를 바탕으로 작성한 코드이다.
#include <iostream> using namespace std; int main() { // char& name; // error : initialization 하지 않았기 때문에 int iNum = 10; int& rNum = iNum; // int * pNum = &iNum; cout << "value:" << iNum << " address:" << &iNum << endl; cout << "value:" << rNum << " address:" << &rNum << endl; return 0; } ---- value:10 address:000000FAC0BBF7E4 value:10 address:000000FAC0BBF7E4
위 코드에서 intializaion을 할 때 첫 줄에서 오류가 생기는 것을 확인할 수 있다. reference type은 이미 있는 값에 대한 별명이기 때문에 initialization을 하지 않은 새로운 변수에 refercence type을 설정하려고 하면 오류가 생기는 것이다. (교수님은 이에 대해서 refererence type에는 null이라는 개념이 없는 것이라고 설명하셨다)
즉 initialization 한 변수에 한해서 reference type이 지정 가능하다.
int& rNum = iNum; 이 줄은 “iNum 변수에 대하여 rNum이라는 별명 지정해 준다” 정도로 이해해 주면 된다.
밑의 코드는 위의 코드에 좀 더 추가해서 적은 것이다. 읽어보고 이해해보자.
#include <iostream> using namespace std; int main() { // char& name; // error : initialization 하지 않았기 때문에 int iNum = 10; int& rNum = iNum; // reference int* pNum = &iNum; // pointer cout << "value:" << iNum << " address:" << &iNum << endl; cout << "value:" << rNum << " address:" << &rNum << endl; cout << "value:" << *pNum << "address:" << pNum << endl; rNum = 5; // 별명으로 넣어도 잘 바뀌는지 cout << "value:" << iNum << " address:" << &iNum << endl; cout << "value:" << rNum << " address:" << &rNum << endl; cout << "value:" << *pNum << " address:" << pNum << endl; *pNum = 50; // 포인터를 사용해도 잘 바뀌는지 cout << "value:" << iNum << " address:" << &iNum << endl; cout << "value:" << rNum << " address:" << &rNum << endl; cout << "value:" << *pNum << " address:" << pNum << endl; int a = 10; int* p; int** pp; p = &a; pp = &p; // reference는 double 못해 return 0; } ---- value:10 address:000000AC1A36FB64 value:10 address:000000AC1A36FB64 value:10 address:000000AC1A36FB64 value:5 address:000000AC1A36FB64 value:5 address:000000AC1A36FB64 value:5 address:000000AC1A36FB64 value:50 address:000000AC1A36FB64 value:50 address:000000AC1A36FB64 value:50 address:000000AC1A36FB64
- Difference of reference and pointer
Pointer와 Reference는 주소에 접근한다는 점에서 비슷하다고 할 수 있다. 하지만 몇 가지 차이가 존재한다.
- 변수 주소와 변수 주소의 주소
포인터는 변수 주소와 변수 주소의 주소가 다르다. 하지만 레퍼런스는 동일하다.
ptr1 == &num &ptr1 != &num ---- ref == num &ref == &num
- 선언과 초기화
포인터는 선언 이후에 초기화를 해주어도 되지만 레퍼런스는 무조건 선언과 동시에 초기화를 해주어야 한다.
- 참조 변수에 접근하는 방법
포인터는 * operator를 사용하여 dereference해서 참조 변수에 접근한다. 레퍼런스는 단순히 변수 이름만으로 참조 변수에 접근한다. (즉 포인터는 *a 형태로 변수에 접근하지만 레퍼런스는 a만으로도 변수에 접근할 수 있다)
- Call by value, Call by reference
swap 함수를 reference를 이용해서 다시 구현해보자. 결과는 밑 코드와 같다.
#include <iostream> using namespace std; void intSwap1(int num1, int num2) { int temp{ num1 }; num1 = num2; num2 = temp; } void intSwap2(int* num1, int* num2) { // using pointer int temp{ *num1 }; *num1 = *num2; *num2 = temp; } void intSwap3(int& num1, int& num2) { // using ref int temp{ num1 }; num1 = num2; num2 = temp; } int main() { int iNum1{ 1 }; int iNum2{ 3 }; cout << iNum1 << " " << iNum2 << endl; intSwap3(iNum1, iNum2); cout << iNum1 << " " << iNum2 << endl; return 0; } ---- 1 3 3 1 // ref를 이용해도 swap이 잘 동작하는 걸 확인할 수 있다.
reference를 이용한 swap 함수는 잘 동작한다.
swap1은 call by value를 이용한 동작이고, swap2와 swap3은 call by reference를 이용한 동작이다.
- etc.. (reference와 관련 없지만 교수님이 함께 설명하셔서 여기 넣어둠)
밑의 코드를 살펴보자.
int* f1() { int iNums[3]{ 1,2,3 }; return iNums; // adress of array } int main(){ int* pNums = f1(); cout << *pNums << endl; return 0; }
출력 시켜보면 1이라는 값이 나옴을 확인할 수 있다.
그럼 만약 *(pNums+1)을 출력시켜보면 어떤 결과가 나올까?
trash 값이 나오는 걸 확인할 수 있다.
이는 함수 자기 자신이 종료되면 함수 내 값들이 모두 deallocate 되기 때문에 발생하는 문제이다.
그럼 이는 어떻게 해결할 수 있을까?
heap allocation을 이용하면 된다.
heap을 이용해서 공간을 할당하고 다 사용한 후 delete 해주면 된다.
#include <iostream> using namespace std; int* f2(){ int* iNums new int[3]; //heap allocation 하면 된다. iNums[0] = 1; iNums[1] = 2; iNums[2] = 3; return iNums; } int main() { int* pNums = f2(); cout << *pNums << endl; cout << *(pNums+1) << endl; cout << *(pNums+2) << endl; delete[] pNums; // 여기서는 안써도 되지만 delete 해주어서 memory 관리를 하는게 좋다. return 0; }
기존의 예제에서는 main 함수가 array를 파라미터로 넘기는 것을 확인해 보았다.
이번에는 함수에서 return으로 array를 넘기는 과정을 보았다.
main 함수는 프로그램이 종료될 때 까지 살아있고 그 외의 함수는 자기 자신이 종료되면 죽기 때문에 파라미터가 array인 것과 return 값이 array인 것에는 차이가 있을 것이다. 주의하도록 하자.
++ c++에서는 life time 때문에 값이 비정상적으로 출력 되는 문제가 많다. static 변수, pointer, reference, dynamic allocation 등을 이용해서 이러한 문제를 해결할 수 있어야 한다.
05. Functional Pointer
Functional Pointer는 직접 부르지 않고 함수의 주소를 호출한다.
#include <iostream> using namespace std; int foo() { return 5; } double goo() { return 6; } int hoo (int n) { return n; } int main() { int (*fcnPtr)() { &foo }; // fcnPtr points to function foo double (*dfcnPtr)(); int (*pfcnPtr)(int); dfcnPtr = &goo; pfcnPtr = &hoo ; cout << fcnPtr() << endl ; cout << dfcnPtr() << endl ; cout << pfcnPtr(8) << endl ; return 0; }
06. Pointer and Const
restriction이 더 작은 걸로 옮기면 error가 생긴다.
const는 전에도 언급했지만 변경할 수 없는 값들이다. 이 const 변수를 변경 가능한 일반 변수에 할당한다면 에러가 생길 것이다. restriction이 더 작은 걸로 옮겨갔기 때문이다.
이걸 바탕으로 밑 코드를 살펴보자.
int value{ 5 }; int* ptr{ &value }; *ptr = 6; // change value to 6 cout << "value: " << value << "*ptr: " << *ptr << endl; ----------------------------------------------------------------------------------------------------- const int value{ 5 }; // value is const int* ptr{ &value }; // compile error: cannot convert const int* to int* *ptr = 6; // change value to 6 cout << "value: " << value << "*ptr: " << *ptr << endl; ----------------------------------------------------------------------------------------------------- const int value{ 5 }; const int* ptr{ &value }; // this is okay, ptr is a non-const pointer that is pointing to a "const int" *ptr = 6; // not allowed, we can't change a const value cout << "value: " << value << "*ptr: " << *ptr << endl; ---------------------------------------------------------------------------------------------------- int value{ 5 }; // value is not constant const int* ptr{ &value }; // this is still okay cout << "value: " << value << "*ptr: " << *ptr << endl;
첫 번째 코드는 잘 돌아간다.
두 번째 코드에서는 에러가 생긴다. const int*를 int로 바꾸려고 했기 때문이다.
세 번째 코드에서는 에러가 생긴다. cont int*를 const int*로 잘 바꾸었지만 dereferencing을 통해 const 변수의 값을 바꾸려고 했기 때문이다.
네 번째 코드는 잘 돌아간다. *int를 const int*로 바꾸었지만 이는 restriction이 커지는 방향으로의 이동이기 때문에 결과에 영향을 미치지 않는다.
06. Class 01
01. Why use class?
array에 들어갈 수 있는 변수 type은 하나 뿐이다.
그러면 여러 type의 변수를 저장하고 싶으면 어떻게 해야할까?
이를 위해 사용하는 것이 Structure와 Class이다.
02. Struct
Structure의 선언은 다음과 같이 한다.

그림을 보면 structure type 안에 다양한 type의 변수들이 저장되어 있는 걸 확인할 수 있다.
실제 사용은 밑과 같이 한다.

우리가 변수를 선언할 때는 ‘int a;’ 형태로 선언한다. structure도 마찬가지이다. ‘(struct + struct 이름) + (우리가 생성할 변수의 이름);’ 과 같은 형태로 선언할 수 있다.
(structure type은 우리가 직접 만드는 type이라고 이해하면 좋을 것 같다)
또는 미리 struct 이름을 정의해 놓고 ‘struct 이름 + 우리가 생성할 변수의 이름’ 형태로 정의할 수도 있다. 이는 위 그림의 오른쪽을 참고하자. 주로 사용하는 형태는 왼쪽이다.
예시 코드를 살펴보자.
#include <iostream> #include <string.h> using namespace std; struct Student { // structure 선언 int id; //int형type 선언 string name; //str형 type 선언 Student* friends; //pointer 선언 }; int main() { Student students[10]; Student* student1 = new Student{ 201911999, "John" , nullptr}; // sturucture형 pointer 작성 (struct도 하나의 type이므로 가능한거임) Student* student2 = new Student{ 5678, "Tom", student1 }; student1->friends = student2; //dereferencing : ->를 이용해서 pointer에 직접 접근한다. // struct의 변수에 접근할 때는 ., 주소에 접근할 때는 -> 쓴다고 생각하기! students[0] = (*student1); //dereferencing을 통항 copy students[1] = (*student2); cout << students[0].name << endl; // students[0]에는 { 201911999, "John" , nullptr}이 data가 담겨있으므로 John이 출력된다. cout << students[1].name << endl; // 마찬가지로 Tom이 출력된다 cout << &students[0] << endl; //이거랑 cout << student1 << endl; // 얘랑 주소가 다르다. 당연히 값만 받아온 것이므로 cout << students[0].friends << endl; // 0번째의 주소 -> John의 friends 정보 : Tom, 즉 student2의 주소 cout << student1->friends << endl; // John의 friends 정보 : Tom, 즉 student2의 주소 cout << student2 << endl; // Tom의 정보 : Tom, 즉 student2의 주소 so 이 세 줄 모두 같은 값 나오게 된다. cout << student1->friends->name; << endl; // student2의 name, Tom이 출력된다. student1->friends는 student2의 주소이므로 '->'를 이용해서 다시 dereferencing 해주었다. cout << students[0].id << endl; // student1의 id cout << (*student1).id << endl; // student1의 id cout << student1->id << endl; // student1의 id cout << sizeof(int) << endl; cout << sizeof(Student) << endl; delete student1; delete student2; return 0; } ---- John Tom 0000004A2C2FF560 000001B9422054B0 000001B942204F50 000001B942204F50 000001B942204F50 Tom 201911999 201911999 201911999 4 56 // code by ahyeon
주석을 읽어보면서 차근차근 이해해 보아야 한다.
여기서 중요히 보아야할 것은 ‘→’와 ‘.’이다. struct 내의 일반 변수에 접근할 때는 .을 이용하고 pointer 변수에 접근할 때는 →을 사용한다.
(참고 : cout 부분에서는 dereferencing이 안되는 것 같다. cout << *student2 << endl;를 하면 오류가 생긴다)
03. Class
- Introduction to OOP
객체지향 프로그래밍은 하나의 프로그래밍 패러다임이다.
- Abstraction
- Encapsulation
- Inheritance
- Polymorphism
객체지향 프로그래밍은 이 네 가지 컨셉을 기본으로 하고 있다. (추상화, 캡슐화, 상속, 다형성)
- Syntax
class도 structure와 마찬가지로 user-defined type이다.
class의 문법은 다음과 같다.

사용은 다음과 같이 할 수 있다.

그런데 structure와 다른 점이 한 가지 있다. class에는 public이라는 키워드가 추가 된 것을 확인할 수 있다. 만약 이 키워드를 없애면 어떻게 될까?
#include <iostream> using namespace std; class Student { // public: //: 찍어야 함 ; 찍으면 안됨 int id; string name; }; int main() { Student* student1; student1 = new Student{ 201911999, "John" }; cout << (*student1).id << endl; cout << student1->id << endl; delete student1; return 0; }
위 코드를 실행하면 error가 생긴다.

class는 public, private, protect 세 가지 키워드를 이용해 class에 속해 있는 모든 specifier의 범위를 지정해준다.
public은 다른 모든 함수에서 class의 specifier에 접근 가능하게 해주고
private는 class 내부에서만 specifier를 사용할 수 있게 한다.
마지막으로 protect는 상속과 관련된 키워드이다. 이는 나중에 자세히 설명하도록 하겠다.
class의 default accessibility는 private이다.
즉 위 코드에서 public을 지워주면 default 값인 private accessibility가 설정된다. 따라서 main 함수에서 class의 specifier에 접근하지 못하게 되기 때문에 에러가 생긴다.
(이것과 별개로 위 코드는 딱히 좋은 코드는 아니다. 교수님은 desirable한 프로그래밍이 아니라고 하셨다)
또 다른 예제 코드를 살펴보자.
class Student { private: int id; string name; public: int GetID() { return id; } }; Student* student1; student1 = new Student{ 201911999, "John" }; cout << (*student1).GetID() << endl; cout << student1->GetID() << endl; delete student1;
오류가 생기는 걸 확인할 수 있다. dynamic allocation을 하면서 private accessibility를 가진 id와 name에 접근하려 했기 때문이다.
#include <iostream> using namespace std; class Student { private: int id; string name; public: int GetID() { return id; } }; int main() { Student* student1; student1 = new Student; cout << (*student1).GetID() << endl; // cout << student1->name << endl; //private 하기 때문에 error 생김 cout << student1->GetID() << endl; // 간접적으로 class 내부에서 받아와서 출력시킬 수 있음 : 초기화 안했기 때문에 아무런 정보는 뜨지 않음 delete student1; return 0; } ----
다음과 같이 수정하면 error가 생기지 않는다.
하지만 넣어준 값이 아무것도 없기 때문에 출력 값은 없다.
만약에 private를 public로 바꾸면 어떤 결과가 출력될까?
#include <iostream> #include <string> using namespace std; class Student { public: int id; string name; int GetID() { return id; } }; int main() { Student* student1; student1 = new Student{ 201911999, "John" }; cout << (*student1).GetID() << endl; cout << student1->GetID() << endl; delete student1; return 0; } ---- 201911999 201911999
위와 같은 결과가 나오게 된다.
여기서 중요하게 보아야 할 부분은
cout << (*student1).GetID() << endl;
cout << student1->GetID() << endl;
여기이다.
Pointer에서 접근할 때는 ‘→’ 기호를 써준 것을 확인할 수 있다. 우리는 여기서 ‘→’가 dereferencing의 의미를 가지고 있다고 해석할 수 있을 것이다 (나만의 해석). 이건 많이 헷갈린다..
04. Constructor / Destructor
- Constructor
constructor는 class 내부의 member function으로 instance화 될 때 자연스럽게 불려지는 함수이다. 파이썬 class의 init 함수와 같다고 생각하면 될 것 같다.
return value는 없고 여러 개의 constructor를 정의할 수 있다.
class의 이름과 같은 이름을 가진 함수를 정의해주면 자동으로 constuctor가 된다.
- Syntax

위와 같이 class 내부에서 constructor를 선언하고 밖에서 constructor를 정의하므로써 사용할 수 있다. class 내부의 constuctor는 heap에 allocation 되고 constructor의 정의는 stack에 allocation 된다.
- Destructor
말 그대로 constructor를 지워주는 것이다. destuctor도 constructor과 마찬가지로 내부에서 선언되고 외부에서 정의된다.
destructor은 한 가지 밖에 없고, default destructor는 compiler에 의해서 정의된다.
(++ constructor과 destructor 모두 내부에서 한 번에 선언과 정의를 해도 문제 되지는 않는다)
- Example
예제 코드를 살펴보자. (음 오류가 많다.. 참고만 하자..)
#include <iostream> #include <string> using namespace std; class Student { private: int* m_pID; string m_name; public: Student(); // Constructor (without parameter) Student(int, string); // Constructor (with parameter) ~Student(); // Destructor int GetID() { return *m_pID; } string GetName() { return m_name; } }; void Student::InitVariables(int, string name) { m_pID = new int(0); m_name = "Alice"; } //Constructor implementation (ver 1) Student::Student() { m_pID = new int(0); m_name = "Alice"; cout << "Constructor w/o parameter: " << m_name << endl; } //Constructor implementation (ver 2) Student::Student(int id, string name) { m_pID = new int(id); m_name = name; cout << "Constructor with parameter: " << m_name << endl; } //Destructor implementation Student::~Student() { cout << "Destructor: " << m_name << endl; delete m_pID; } int main() { Student s1; s1.InitVariables(123, "Alice"); Student s2; s2.InitVariables(456, "Tom"); cout << s1.GetID() << " " << s1.GetName() << endl; cout << s2.GetID() << " " << s2.GetName() << endl; cout << "Place 1" << endl; Student s1; Student s2(2, "Jenny"); cout << "Place 2" << endl; Student* s3; cout << "Place 3" << endl; s3 = new Student(4, "Tom"); cout << "Place 4" << endl; delete s3; cout << "Place 5" << endl; cout << "Place 1" << endl; Student s1; //Stack allocation cout << "Place 2" << endl; // 이후 main 함수가 return 될 때 스택에 allocation 되어있던거 deallocation되면서 destructor 자동으로 불려짐 cout << "Place 1" << endl; Student* s1; cout << "Place 2" << endl; s1 = new Student(123, "John"); cout << "Place 3" << endl; delete s1; // Heap deallocation cout << "Place 1" << endl; //Student s1(123, "Tom"); Student* s1; // Student형 포인터 타입을 설정 -> 실제로 Student형은 아니다, 자리만 만든거// Pointer at Stack cout << "Place 2" << endl; //cout << s1.GetID() << " " << s1.GetName() << endl; Student* s2; // Pointer at Stack cout << "Place 3" << endl; s1 = new Student(123, "John"); //heap allocation(ver 2) cout << "Place 4" << endl; s2 = new Student(); // Heap allocation(ver 1) cout << "Place 5" << endl; Student s3; // Stack allocation cout << "Place 6" << endl; Student s4(456, "Jenny"); cout << "Place 7" << endl; Student many_stdents[3]; // stack allocation cout << "Place 8" << endl; return 0; } // code by Ahyeon
밑의 코드도 주의깊게 살펴보도록 하자. 전년도 기출 문제이다.
#include <iostream> #include <string> using namespace std; class Vehicle { private: string id; int mileage; public: Vehicle() { mileage = 10; id = "None"; cout << "Const [1]: " << id << endl; } Vehicle(int m) : Vehicle(m, "None") { // cons[4] constructor에 다시 접근 cout << "Const [2]: " << id << endl; } Vehicle(string c) : Vehicle(10, c) { cout << "Const [3]: " << id << endl; } Vehicle(int m, string c) { mileage = m; id = c; cout << "Const [4]: " << id << endl; } ~Vehicle() { cout << "Destructor: " << id << endl; } string getID(){ return id; } void setID(string c) { id = c; } int getMileage() { return mileage; } }; int main() { Vehicle car1; car1.setID("BMW"); Vehicle* car2 = new Vehicle(5000); car2->setID("GM"); Vehicle car3(3000, "TESLA"); Vehicle* v_arr[2] = { &car1, &car3}; for (int i = 0; i < 2; i++) { cout<<v_arr[i]->getID()<<"\t"<<v_arr[i]->getMileage()<<endl; } cout<<car2->getID()<<"\t"<<car2->getMileage()<<endl; delete car2; return 0; } ----
05. Member Variable Initialization
member variable를 초기화 하는 방법이다.
#include <iostream> using namespace std; class Student { private: int* m_pID; string m_name; public: void InitVariables(int, string); }; void Student::InitVariables(int id, string name) { this->m_pID = new int(id); m_name = name; } int main() { Student s1; s1.InitVariables(201911999, "Alice"); Student s2; s2.InitVariables(201911998, "Bob"); return 0; }
#include <iostream> #include <string> using namespace std; class Student{ private: int* m_pId; string m_name; public: Student(); //Constructor 1 Student(int, string); //Constructor 2 ~Student(); //Destructor int GetId() { return *m_pId; } void SetID(int); }; void Student::SetID(int ID) { //*m_pId = ID; //이런방법도 있고 *(this->m_pId) = ID; } //Constructor 1 implementation Student::Student() : Student(0, "") { // 두 번째 construct가 먼저 실행되기 때문에 construct2가 먼저 실행된다 cout << "Constructor 1" << m_name << endl; } //Constructor 2 implementation Student::Student(int id, string name) { m_pId = new int{ id }; m_name = name; cout << "Constructor 2" << m_name << endl; } //Destructor implementation Student::~Student() { if (m_pId != nullptr) { delete m_pId; m_pId = nullptr; } cout << "Destructor " << m_name << endl; } int main() { Student s1; //Stack allocation Student* s2; //Pointer s2 = new Student(123, "Jenny"); //Heap allocation cout << s2->GetId() << endl; s2->SetID(456); //update cout << s2->GetId() << endl; delete s2; //Heap deallocation return 0; } // code by Ahyeon
06. This Pointer
자기 자신을 가르키는 상수
#include <iostream> #include <string> using namespace std; class Student { private: int* m_pId; string m_name; public: Student(); //Constructor 1 Student(int, string); //Constructor 2 ~Student(); //Destructor int GetID() { return *m_pId; } void SetID(int); }; //Constructor 1 implementation Student::Student() : Student(0, "") { cout << "Constructor 1" << m_name << endl; } //Constructor 2 implementation Student::Student(int id, string name) { m_pId = new int{ id }; m_name = name; cout << "Constructor 2" << m_name << endl; } //Destructor implementation Student::~Student() { if (m_pId != nullptr) { delete m_pId; m_pId = nullptr; } cout << "Destructor " << m_name << endl; } int main() { Student s1; //Stack allocation Student* s2; //Pointer s2 = new Student(123, "Jenny"); //Heap allocation cout << s2->GetID() << endl; s2->SetID(456); cout << s2->GetID() << endl; delete s2; //Heap deallocation return 0; }
07. Class 02
01. UML Diagram
- UML diagram
UML diagram은 class를 디자인 하기 위한 하나의 graphical design diagram이다.
- classes
- attributes
- operations
- the relationship among objects
를 표현한다.

위 사진 처럼 맨 위 구역에는 class, 그 밑 구역에는 attribute, 맨 아래 구역에는 operation을 넣어 표현한다. 추가로 the relationship among objects는 화살표를 이용해 표현한다.
실제 코드를 바탕으로 다이어그램을 그려보자.

위 그림을 보면 private members는 (-), public members는 (+), parameter type은 괄호 안에, return type은 : 뒤에 적는 것을 확인할 수 있다.
위에서 the relationship among objects는 화살표로써 표현한다고 언급했다. 관계에 따라서 서로 다른 화살표 모양을 쓰는데 이는 다음 그림과 같다.

(여기서 association을 has a relationship이라고 부르기도 한다. 교수님께서 말씀해주셔서 추가로 적어둔다)
02. Practice
01. UML Diagram 내용을 바탕으로 연습을 해보자.
우리는 밑의 다이어그램을 바탕으로 코드를 짜볼 것이다.

Lecture.cpp
//Lecture.cpp #include "Lecture.h" Lecture::Lecture() { m_pId = new int(0); m_name = "Alice"; } Lecture::Lecture(int id, string name) { m_pId = new int(id); m_name = name; } Lecture::Lecture(int id, string name, Teacher* t) { m_pId = new int(id); m_name = name; this->m_Teacher = t; } string Lecture::GetName() { return m_name; } void Lecture::SetID(int id) { *(this->m_pId) = id; } string Lecture::GetTeacherName() { return this->m_Teacher->GetName(); }
Lecture.h
#pragma once //Lecture.h #include <iostream> #include <string> #include "Student.h" #include "Teacher.h" // Added class Student; class Teacher; using namespace std; class Lecture { private: int* m_pId; string m_name; Student* s_group; //A list of students Teacher* m_Teacher; //Added public: Lecture(); Lecture(int, string); Lecture(int, string, Teacher* t); //Added string GetName(); void SetID(int id); string GetTeacherName(); //Added };
Student.cpp
//Student.cpp #include "Student.h" Student::Student() { m_pId = new int(0); m_name = "Alice"; } Student::Student(int id, string name) { m_pId = new int(id); m_name = name; } string Student::GetName() { return m_name; } void Student::SetID(int id) { *(this->m_pId) = id; }
Student.h
#pragma once #include <iostream> #include <string> #include "Lecture.h" class Lecture; using namespace std; class Student { private: int* m_pId; string m_name; Lecture* m_lName; public: Student(); Student(int, string); string GetName(); void SetID(int id); };
Teacher.cpp
//Teacher.cpp #include "Teacher.h" Teacher::Teacher() { m_pId = new int(0); m_name = "Alice"; } Teacher::Teacher(int id, string name) { m_pId = new int(id); m_name = name; } string Teacher::GetName() { return m_name; } void Teacher::SetID(int id) { *(this->m_pId) = id; }
Teacher.h
#pragma once //Teacher.h #include "Lecture.h" #include <string> class Lecture; using namespace std; class Teacher { private: int* m_pId; string m_name; Lecture* m_lecture; public: Teacher(); Teacher(int, string); string GetName(); void SetID(int id); };
main
#include <iostream> #include <string> using namespace std; class Student { private: int* m_pId = nullptr; string m_name = ""; public: static int StudentCnt; //Added Student(int, string); //Constructor Student(const Student& rhs) { //Copy Constructor this->m_pId = new int(*rhs.m_pId); //Heap allocation this->m_name = rhs.m_name; cout << "Copy Constructor: " << this->m_name << endl; Student::StudentCnt++; //Added } ~Student(); int Get_ID() { return *(this->m_pId); } int* Get_ID_Addr() { return this->m_pId; } string Get_Name() { return this->m_name; } }; int Student::StudentCnt = 0; //Static variable init Student::Student(int id, string name) : m_pId{ new int{id} }, m_name{ name } { cout << "Default Constructor: " << this->m_name << endl; Student::StudentCnt++; //Added } Student::~Student() { delete m_pId; cout << "Destructor: " << this->m_name << endl; Student::StudentCnt--; //Added } void printStudentName(Student s) { //Call by Value cout << s.Get_Name() << endl; } void printStudentName2(Student* s) { //Call by Reference cout << s->Get_Name() << endl; } int main() { int iNum1 = 10; int iNum2{ iNum1 }; Student s1(201911999, "Alice"); //Student::StudentCnt++; Student s2{ s1 }; //Student::StudentCnt++; cout << "s1: " << s1.Get_Name() << " " << s1.Get_ID() << " " << s1.Get_ID_Addr() << endl; cout << "s2: " << s2.Get_Name() << " " << s2.Get_ID() << " " << s2.Get_ID_Addr() << endl; cout << "Before print (call by value)" << endl; printStudentName(s1); cout << "After print (call by value)" << endl; cout << "Before print (call by Reference)" << endl; printStudentName2(&s1); cout << "After print (call by Reference)" << endl; cout << "Total Student: " << Student::StudentCnt << endl; return 0; }
main 함수에서 파일을 실행시켜 보면 원하는 결과가 나올 것이다.
질문 : 헤더 파일과 cpp 파일로 구분하는 이유는 무엇일까?
헤더 파일은 ‘정의’, c++은 ‘선언’을 한다. 그런데 왜 굳이 구분을 하는 것일까?
08. Polymorphism 01
01. Problem of default Copy
간단한 예제 코드를 살펴보며 copy에 대한 이해를 해보자.
#include <iostream> using namespace std; class Student { private: int* m_pId = nullptr; string m_name = ""; public: Student(int, string); ~Student(); int Get_ID() { return *(this->m_pId); } int* Get_ID_Addr() { return this->m_pId; } string Get_Name() { return this->m_name; } }; Student::Student(int id, string name) : m_pId{ new int{id} }, m_name{ name }{} Student::~Student() { delete m_pId;} int main() { int iNum1 = 10; int iNum2{ iNum1 }; Student s1(201911999, "Alice"); Student s2{ s1 }; cout << "s1: " << s1.Get_Name() << " " << s1.Get_ID() << " " << s1.Get_ID_Addr() << endl; cout << "s2: " << s2.Get_Name() << " " << s2.Get_ID() << " " << s2.Get_ID_Addr() << endl; return 0; }
위 코드를 실행 시켜보면 출력은 제대로 되지만 run time error가 생기는 걸 확인할 수 있다.
shallow copy와 deep copy 개념을 통해 이유를 알아볼 수 있다.
밑 사진과 같이 copy를 하게 되면 S1과 S2는 같은 주소를 가르키게 된다. 이 때까지는 아무 문제가 발생하지 않는다. 하지만 이후 deconstructor에 의해서 S2가 delete된 후 S1이 delete 되는데 S1과 S2는 같은 주소를 가르키고 있었으므로 오류가 생긴다 (이미 지워진 것에 대해서 다시 지우는 꼴이 되므로).
추가로 ppt에 이러한 default는 shallow copy를 한다고 나와있다. 즉 값만 copy 된다는 것이다. 이를 바탕으로 위 그림을 다시 보자. 밑 그림을 참고 한다면 더 이해가 잘 될 것이다.

02. Copy constructor
그래서 위와 같은 문제를 막기 위해서 copy construct를 정의한다.
copy constructor는 heap 영역에 따로 공간을 할당하기 때문에 copy한 객체에는 기존 객체와 다른 주소가 할당 된다.


위 그림을 참고하자.
예제 코드를 살펴보자.
**이거 코드 필기 제대로 안되어있음 수정 필요함.
여기서 핵심은 언제 copy constructor가 불려지는지이다. 이를 유의해서 보도록하자.
#include <iostream> using namespace std; class Student { private: int* m_pId = nullptr; string m_name = ""; public: Student(int, string); // Constructor Student(const Student& rhs) { // copy Constructor this->m_pId = new int(*rhs.m_pId); this->m_name = rhs.m_name; } ~Student(); int Get_ID() { return *(this->m_pId); } int* Get_ID_Addr() { return this->m_pId; } string Get_Name() { return this->m_name; } }; Student::Student(int id, string name) : m_pId{ new int{id} }, m_name{ name } { cout << "Default Constructor: " << this->m_name << endl; } Student::~Student() { delete m_pId; }{ delete m_pld; cout << "Destructor: " << this->m_name << endl; } void printstudentname(Student s) { cout << s.Get_Name() << endl; } void printstudentname(Student *s) { cout << s->Get_Name() << endl; } int main() { int iNum1 = 10; int iNum2{ iNum1 }; Student s1(201911999, "Alice"); Student s2{ s1 }; cout << "s1: " << s1.Get_Name() << " " << s1.Get_ID() << " " << s1.Get_ID_Addr() << endl; cout << "s2: " << s2.Get_Name() << " " << s2.Get_ID() << " " << s2.Get_ID_Addr() << endl; cout << "Before print (call by value)" << endl; printstudentname(s1); cout << "After print (call by value)" << endl; cout << "Before print (call by reference)" << endl; printstudentname(&s1); cout << "After print (call by reference)" << endl; return 0; }
03. Review
#include <iostream> using namespace std; class Student { private: int* m_pId = nullptr; string m_name = ""; public: static int StudentCnt; // 스태틱 변수 :global한 영역에 존재 Student(int, string); // 컨스트럭트 Student(const Student& rhs); // 카피 컨스트럭트 ~Student(); // 디스트럭스 // member function int Get_ID() { return *(this->m_pId); } int* Get_ID_Addr() { return this->m_pId; } string Get_Name() const { return this->m_name; } void Set_Name(string newName) { this->m_name = newName; } }; Student::Student(int id, string name) : m_pId{ new int{id} }, m_name{ name } {} // 컨스트럭스 Student::~Student() { delete m_pId; } // 디컨스트럭스 Student::Student(const Student& rhs) { // 카피 this->m_pId = new int(*rhs.m_pId); // heap 영역에 할당 this->m_name = rhs.m_name; // stack에 할당 } int Student::StudentCnt = 0; // static 변수 초기화 int main() { int iNum1 = 10; int iNum2{ iNum1 }; // 카피 Student s1(201911999, "Alice"); // 객체 생성 Student::StudentCnt++; // main 함수에서 스테틱 변수 사용하는 방법 Student s2(s1); Student::StudentCnt++; cout << "s1: " << s1.Get_Name() << " " << s1.Get_ID() << " " << s1.Get_ID_Addr() << endl; cout << "s2: " << s2.Get_Name() << " " << s2.Get_ID() << " " << s2.Get_ID_Addr() << endl; cout << "Total students: " << Student::StudentCnt << endl; return 0; } ---- s1: Alice 201911999 0000028000B75AA0 s2: Alice 201911999 0000028000B77130 Total students: 2
04. Const members / friend
바뀌지 않아야 하는 값
return 값과 이름 사이에 const 키워드를 정의하면 된다.
include <iostream> using namespace std; class Student { private: int* m_pId = nullptr; string m_name = ""; public: Student(int, string); Student(const Student& rhs); ~Student(); int Get_ID() { return *(this->m_pId); } int* Get_ID_Addr() { return this->m_pId; } string Get_Name() const { return this->m_name; } void Set_Name(string newName) const { this->m_name = newName; } // const 업데이트 하려고 했기 때문에 error }; Student::Student(int id, string name) : m_pId{ new int{id} }, m_name{ name } {} // 컨스트럭스 Student::~Student() { delete m_pId; } // 디컨스트럭스 Student::Student(const Student& rhs) { // 카피 this->m_pId = new int(*rhs.m_pId); // heap 영역에 할당 this->m_name = rhs.m_name; // stack에 할당 } int Student::StudentCnt = 0; // static 변수 초기화 int main() { int iNum1 = 10; int iNum2{ iNum1 }; // 카피 Student s1(201911999, "Alice"); // 객체 생성 Student::StudentCnt++; // main 함수에서 스테틱 변수 사용하는 방법 Student s2(s1); Student::StudentCnt++; cout << "s1: " << s1.Get_Name() << " " << s1.Get_ID() << " " << s1.Get_ID_Addr() << endl; cout << "s2: " << s2.Get_Name() << " " << s2.Get_ID() << " " << s2.Get_ID_Addr() << endl; cout << "Total students: " << Student::StudentCnt << endl; return 0; }
friend 키워드 이용해서 private에서 예외 사항 줌
예외 사항을 만들고 싶을 때 이를 활용한다.
#include <iostream> using namespace std; class Teacher { private: int* m_pId; string m_name; string teacher_hobby = "Play a Video Game"; friend string GetTeacherHobby(Teacher t) { // friend 키워드 return t.teacher_hobby; }; }; class Student { private: int* m_pId = nullptr; string m_name = ""; public: static int StudentCnt; // 스태틱 변수 :global한 영역에 존재 Student(int, string); Student(const Student& rhs); ~Student(); int Get_ID() { return *(this->m_pId); } int* Get_ID_Addr() { return this->m_pId; } string Get_Name() { return this->m_name; } void Set_Name(string newName) { this->m_name = newName; } void ShowTeacherHobby(Teacher t) { // friend 키워드 때문에 가능해짐 cout << GetTeacherHobby(t) << endl; } }; Student::Student(int id, string name) : m_pId{ new int{id} }, m_name{ name } {} // 컨스트럭스 Student::~Student() { delete m_pId; } // 디컨스트럭스 Student::Student(const Student& rhs) { // 카피 this->m_pId = new int(*rhs.m_pId); // heap 영역에 할당 this->m_name = rhs.m_name; // stack에 할당 } int Student::StudentCnt = 0; // static 변수 초기화 int main() { int iNum1 = 10; int iNum2{ iNum1 }; // 카피 Student s1(201911999, "Alice"); // 객체 생성 Student::StudentCnt++; // main 함수에서 스테틱 변수 사용하는 방법 Student s2(s1); Student::StudentCnt++; cout << "s1: " << s1.Get_Name() << " " << s1.Get_ID() << " " << s1.Get_ID_Addr() << endl; cout << "s2: " << s2.Get_Name() << " " << s2.Get_ID() << " " << s2.Get_ID_Addr() << endl; cout << "Total students: " << Student::StudentCnt << endl; Teacher t1; cout << GetTeacherHobby(t1) << endl; // friend 때문에 가능 s1.ShowTeacherHobby(t1); return 0; }
05. Polymorphism
다형성
컨스트럭트 오버 로딩
#include <iostream> using namespace std; class Complex { int* m_r = nullptr; // real part int* m_i = nullptr; // imaginary part public: Complex(); Complex(int, int); Complex(int); ~Complex(); Complex(const Complex& rhs); void print() const; }; Complex::Complex(int r, int i) { m_r = new int(r); m_i = new int(i); } Complex::~Complex() { if (m_r) delete m_r; if (m_i) delete m_i; m_r = m_i = nullptr; } Complex::Complex() : Complex(0, 0) { } Complex::Complex(int r) : m_r{ new int(r) }, m_i{ new int(0) } { } Complex::Complex(const Complex& rhs) : Complex(*rhs.m_r, *rhs.m_i) { } void Complex::print() const { cout << *m_r << (*m_i < 0 ? "" : "+") << *m_i << "j" << endl; } int main() { Complex c1; // 파라미터 x 버전 Complex c2(3, 1234); Complex c3(-3); Complex c4(c2); c1.print(); c2.print(); c3.print(); c4.print(); return 0; } ---- 0+0j 3+1234j -3+0j 3+1234j
연산자 오버 로딩
#include <iostream> using namespace std; class Complex { int* m_r = nullptr; // real part int* m_i = nullptr; // imaginary part public: Complex(); Complex(int, int); Complex(int); ~Complex(); Complex(const Complex& rhs); void print() const; }; Complex Complex::Complex(int r, int i) { m_r = new int(r); m_i = new int(i); } Complex::~Complex() { if (m_r) delete m_r; if (m_i) delete m_i; m_r = m_i = nullptr; } Complex::Complex() : Complex(0, 0) { } Complex::Complex(int r) : m_r{ new int(r) }, m_i{ new int(0) } { } Complex::Complex(const Complex& rhs) : Complex(*rhs.m_r, *rhs.m_i) { } void Complex::print() const { cout << *m_r << (*m_i < 0 ? "" : "+") << *m_i << "j" << endl; } int main() { Complex c1; // 파라미터 x 버전 Complex c2(3, 1234); Complex c3(-3); Complex c4(c2); c1.print(); c2.print(); c3.print(); c4.print(); Complex c5(c2 + c2); c5.print(); return 0; }
(코드 다시 적기) → + 를 따로 정의해 주어야지 정상적으로 동작한다
연산자 오버로딩 again
#include <iostream> using namespace std; class Complex { int* m_r = nullptr; // real part int* m_i = nullptr; // imaginary part public: Complex(); Complex(int, int); Complex(int); ~Complex(); Complex(const Complex& rhs); void print() const; }; Complex Complex::Complex(int r, int i) { m_r = new int(r); m_i = new int(i); } Complex::~Complex() { if (m_r) delete m_r; if (m_i) delete m_i; m_r = m_i = nullptr; } Complex::Complex() : Complex(0, 0) { } Complex::Complex(int r) : m_r{ new int(r) }, m_i{ new int(0) } { } Complex::Complex(const Complex& rhs) : Complex(*rhs.m_r, *rhs.m_i) { } void Complex::print() const { cout << *m_r << (*m_i < 0 ? "" : "+") << *m_i << "j" << endl; } int main() { Complex c1; // 파라미터 x 버전 Complex c2(3, 1234); Complex c3(-3); Complex c4(c2); c1.print(); c2.print(); c3.print(); c4.print(); Complex c5(c2 + c2); c5.print(); Complex c7(c2 + 5.2); c7.print(); return 0; }
왼쪽에 ‘객체가 아닌 것’이 오는 경우
#include <iostream> using namespace std; class Complex { int* m_r = nullptr; // real part int* m_i = nullptr; // imaginary part public: Complex(); Complex(int, int); Complex(int); ~Complex(); Complex(const Complex& rhs); void print() const; }; Complex Complex::Complex(int r, int i) { m_r = new int(r); m_i = new int(i); } Complex::~Complex() { if (m_r) delete m_r; if (m_i) delete m_i; m_r = m_i = nullptr; } Complex::Complex() : Complex(0, 0) { } Complex::Complex(int r) : m_r{ new int(r) }, m_i{ new int(0) } { } Complex::Complex(const Complex& rhs) : Complex(*rhs.m_r, *rhs.m_i) { } void Complex::print() const { cout << *m_r << (*m_i < 0 ? "" : "+") << *m_i << "j" << endl; } int main() { Complex c1; // 파라미터 x 버전 Complex c2(3, 1234); Complex c3(-3); Complex c4(c2); c1.print(); c2.print(); c3.print(); c4.print(); Complex c5(c2 + c2); c5.print(); Complex c7(c2 + 5.2); c7.print(); Complex c8(8.3 + c2); c8.print(); return 0; }
왼쪽에 오는 버전과 오른쪽에 오는 버전을 명시적으로 구분해서 구현해줄 필요가 있다.
#include <iostream> #include <string> using namespace std; class Complex { int* m_r = nullptr; // real part int* m_i = nullptr; // imaginary part public: Complex(); Complex(int, int); Complex(int); ~Complex(); Complex(const Complex& rhs); void print() const; Complex operator+(const Complex& c2); Complex operator+(int r); Complex operator+(double r); // 해당하는 클래스의 멤버 펑션으로 구분 (왼쪽에는 this 오른쪽에는 파라미터) friend Complex operator+(double r, const Complex& c); // left에 객체가 오지 않는 경우 : friend 이용 }; //Operator overloading Complex Complex::operator+(const Complex& c2) { Complex result; *(result.m_r) = *(m_r)+*(c2.m_r); *(result.m_i) = *(m_i)+*(c2.m_i); return result; } Complex Complex::operator+(int r) { Complex result; *(result.m_r) = *(m_r)+r; *(result.m_i) = *(m_i); return result; } Complex Complex::operator+(double r) { return Complex((static_cast<int> (r)) + *(m_r), *(m_i)); } Complex operator+(double r, const Complex& c) { Complex result((static_cast<int> (r)) + *(c.m_r), *(c.m_i)); return result; } Complex::Complex(int r, int i) { m_r = new int(r); m_i = new int(i); } Complex::~Complex() { if (m_r) delete m_r; if (m_i) delete m_i; m_r = m_i = nullptr; } Complex::Complex() : Complex(0, 0) { } Complex::Complex(int r) : m_r{ new int(r) }, m_i{ new int(0) } { } Complex::Complex(const Complex& rhs) : Complex(*rhs.m_r, *rhs.m_i) { } void Complex::print() const { cout << *m_r << (*m_i < 0 ? "" : "+") << *m_i << "j" << endl; } int main() { Complex c1; Complex c2(3, 1234); Complex c3(-3); Complex c4(c2); c1.print(); c2.print(); c3.print(); c4.print(); Complex c5(c2 + c3); c5.print(); Complex c6(c2 + 4); c6.print(); Complex c7(c2 + 5.2); c7.print(); Complex c8(8.3 + c2); c8.print(); return 0; } /* class Teacher { private: int* m_pId; string m_name; string teacher_hobby = "Play a Video Game"; friend class Student; //Friend class string GetTeacherHobby(Teacher t) { return t.teacher_hobby; }; //friend string GetTeacherHobby(Teacher t) { return t.teacher_hobby; }; }; class Student { private: int* m_pId = nullptr; string m_name = ""; public: void ShowTeacherHobby(Teacher t) { cout << t.teacher_hobby << endl; cout << t.GetTeacherHobby(t) << endl; //cout << GetTeacherHobby(t) << endl; } static int StudentCnt; //static variable Student(int, string); //Constructor Student(const Student& rhs); //Copy Constructor ~Student(); //Destructor int Get_ID() { return *(this->m_pId); } int* Get_ID_Addr() { return this->m_pId; } string Get_Name() const { //cout << Get_ID() << endl; return this->m_name; } void Set_Name(string newName) { this->m_name = newName; } }; Student::Student(int id, string name) : m_pId{ new int{id} }, m_name{ name }{} Student::~Student() { delete m_pId; } Student::Student(const Student& rhs) { this->m_pId = new int(*rhs.m_pId); this->m_name = rhs.m_name; } int Student::StudentCnt = 0; int main() { Student s1(201911999, "Alice"); Teacher t1; //cout << GetTeacherHobby(t1) << endl; s1.ShowTeacherHobby(t1); int iNum1 = 10; int iNum2{ iNum1 }; Student s1(201911999, "Alice"); Student::StudentCnt++; Student s2(s1); Student::StudentCnt++; cout << "s1: " << s1.Get_Name() << " " << s1.Get_ID() << " " << s1.Get_ID_Addr() << endl; cout << "s2: " << s2.Get_Name() << " " << s2.Get_ID() << " " << s2.Get_ID_Addr() << endl; cout << "Total students: " << Student::StudentCnt << endl; return 0; } */
==, +=
#include <iostream> #include <string> using namespace std; class Complex { int* m_r; // real part int* m_i; // imaginary part public: // Constructors (omitted) // + operator overloading (omitted) bool operator==(const Complex &); void operator+=(const Complex &); }; bool Complex::operator==(const Complex & rhs) { if ((*m_r == *rhs.m_r) && (*m_i == *rhs.m_i)) return true; else return false; } void Complex::operator+=(const Complex & rhs) { *m_r += *rhs.m_r; *m_i += *rhs.m_i; } int main() { Complex c2(3, 1234); Complex c9(3, 1234); if (c9 == c2){ cout << "true" << endl; } else{ cout << "false" << endl; } c9 += c2 cout << c9 << endl; return 0;
(교수님 코드 보고 고치기)
09. Polymorphism 02
10. Inheritance 01
HW1
01. Bubble sort
#include <iostream> using namespace std; void mySort(int arr[], int n) { int i; int j; int temp; for (i = 0; i < n; i++) { for (j = 0; j < n-i-1; j++) { if (arr[j] > arr[j + 1]) { temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } void printArray(int arr[], int size) { int i; for (i = 0; i < size; i++) { cout << (arr[i]) << ' '; } } int main() { int arr[] = { 3, 1, 2, 5, 6, 7,10 }; int n = sizeof(arr) / sizeof(arr[0]); mySort(arr, n); cout << "Sorted array: \n"; printArray(arr, n); return 0; }
02. Recursive function
/* 위의 코드: 위의 코드에서는 function이 계속 stack에 쌓이게 된다. 이 때 stack과 heap이 만나면서 exceptional한 결과가 출력 되게 된다. 아래 코드: loop는 stack 메모리를 초과할 만큼 크기가 크지 않기 때문에 정상적으로 돌아간다. */
#include<iostream> using namespace std; void reverse(const char* str) { int i; for (i = strlen(str); i > -1; i--) { cout << *((str)+i); } } int main() { const char* str = "DGIST"; reverse(str); return 0; }
03. Special string filter function
#include <iostream> using namespace std; char** special_filter(const char* sentences[], int filter_offset, int num_sentences) int x; int y; // result에 값 추가하기 위한 pointer const char shap = 35; char** result = new char* [num_sentences]; // array에 변수 넣기 위해 동적할당 for (x = 0; x < num_sentences; x++) { // sentence마다 분석 char* one_sentence = new char[strlen(sentences[x]) + 1]; // 다시 한 번 동적할당 for (y = 0; y < strlen(sentences[x]); y++) { if (*(sentences[x] + y) == ' ') { one_sentence[y] = ' '; } else { if (((0 + 65) <= *(sentences[x] + y) && *(sentences[x] + y) < (filter_offset + 65)) || ((0 + 97) <= *(sentences[x] + y) && *(sentences[x] + y) < (filter_offset + 97))) { one_sentence[y] = *(sentences[x] + y); } else if (((filter_offset + 65) <= *(sentences[x] + y) && *(sentences[x] + y) <= (filter_offset + 90)) || ((filter_offset + 97) <= *(sentences[x] + y) && *(sentences[x] + y) <= (filter_offset + 122))) { one_sentence[y] = '#'; } } } one_sentence[strlen(sentences[x])] = '\0'; // null 종료 문자 추가 result[x] = one_sentence; } return result; } int main() { const char* sentences[] = { "Hello bruhs", "I am ABCDEFGHIJKLMNOPQRSTUBWXYZ" }; int num_sentences = 2; char** result = special_filter(sentences, 6, num_sentences); for (int i = 0; i < num_sentences; i++) { cout << result[i] << endl; } return 0; }
04. Sum of First [n] Odd numbers
#include <iostream> // using namespace std; int nOddSum(int n) { if (n == 0) { return 1; // return 0;으로 바뀌어야 한다. } if (n % 2 == 0) { return n; // return nOddSum(n-1);로 바뀌어야 한다. } else { int res = n + nOddSum(n - 1); return res; } } int main() { int n = 9; int sum = nOddSum(n); cout << "Sum of Odd Numbers = " << sum; return 0; }
05. Dynamic Memory Allocation
#include <iostream> using namespace std; int main() { int** p_arr = new int* [3]; // 12bytes int num[] = {0, 0, 0}; for (int i = 0; i < 3; i++) { cout << "Enter the number of elements: "; cin >> num[i]; p_arr[i] = new int[num[i]]; // 8bytes,12bytes,16bytes } cout << "Finish" << endl; //B. IMPLEMENT DEALLOCATION ROUTINE return 0; } ------------------------------------------------------------------------ Enter the number of elements: 2 Enter the number of elements: 3 Enter the number of elements: 4 Finish
- 2,3,4 입력 후, Finish가 출력 되었을 때 heap에 얼마의 메모리가 차있을지
: 48bytes
int형 p_arr를 동적 할당 하면서 4bytes*3 = 12bytes를 heap에 할당해 주었고, for loop를 수행하며, p_arr의 세 인덱스에 각각 8bytes(int형 4bytes*2), 12bytes(int형 4bytes*3), 16bytes(int형 4bytes*4)을 할당해 주었으므로 12+8+12+16 = 48, 총 48bytes를 heap에 할당해 주었다. 따라서 deallocate되기 전인 “Finish” 출력 단계에서는 총 48bytes의 memory가 heap에 allocate되어있을 것이다.
- deallocate implement 작성하기
:
for (int i = 0; i < 3; i++) { delete[] p_arr[i]; } delete[] p_arr;
06. Pointer arithmetic
#include <iostream> using namespace std; int main() { //Print NEWS const char* sentence[] = { "HELLO CLASS", "GOOD NIGHT WORLD", "BE HAPPY" }; //0번째 주소가 hello~ 가르키고, ... cout << (char)*(sentence[1] + 5); cout << (char)(*(*(sentence + 2) + (1))); cout << (char)(*(*(sentence + (1)) + 11)); cout << (char)(*(*sentence + (9))); return 0; }
a = 1,b = 1,c = 1,d = 9
HW2
01. Programming assignment
To-do
pub/sub 시스템 구현하기
출판자/구독자 시스템
구독자 : 여러 출판자를 구독할 수 있다. 또한 구독 취소를 할 수 있다. 구독 취소를 하면 더 이상 contents를 볼 수 없다.
출판자 : 출판자 또한 여러 구독자를 가질 수 있다. 또한 이상한 구독자들을 지울 수 있다.
모든 함수를 다 사용해야 하고
추가하고 싶은 함수가 있다면 추가할 수 있다.
- 모든 함수 구현해야 함
- main 함수 이용해서 점검 하면 됨
- 다른 추가의 프린트 넣는 것 허용 됨
- 컴파일은 무조건 가능해야 함
- Cpp 파일 하나로만 제출해야 함
- implement를 엄격하게 지킬 필요는 없음
코드 해석
#include <iostream> using namespace std; class IArticlePub { private: string pub_name; // 출판자의 name 저장 (ex. BBC, CNN ...) int pub_id; // 출판자의 id 저장 (id는 출판자마다 unique 해야한다. 또한 n번째 출판자 id는 n이 되어야 한다) string recent_contents; // 출판자에 의해서 생성된 가장 최근의 contents 저장 -> updatePubContents 함수에 의해서 업데이트 되고, 초기 상태는 empty IArticleSub* sub_list[5]; // 출판자를 구독하고 있는 구독자의 리스트 (이 문제에서 구독자는 최대 5명이라고 가정한다) int numOfSub = 0; // 현재 구독자의 명 수 저장 (초기 상태에서는 0명이다) static int static_pub_counter; // 현재까지 생성된 IArticlePub 객체의 갯수를 알려주는 static 변수 public: IArticlePub(const string name); // constructor : 1. 파라미터로 pub 이름 초기화, 2.id 초기화 (static 변수를 이용해서 초기화 하면 될 것 같다), 3. output 보고 이에 맞는 정보 출력 IArticlePub(const string name, const string con); // constructor : 위 1.,2.,3. 과정 똑같이 수행 + string con으로 recent_contents 업데이트 ~IArticlePub(); // destructor : 1. delete 모든 sub_list의 item, 2. print (어떤 걸 print 해야하는 지는 IArticleSub의 DetachResponse(IArticlePub*p_pub) 참고하기) void NotifyAttach(IArticleSub* subscriber); // sub_list에 새로운 구독자 추가 (이 때 구독자의 출판자 리스트 pub_list에도 동시에 추가해 주어야 한다. AttachResponse(IArticlePub* publisher) 참고하기) void NotifyAttachResponse(IArticleSub* subscriber); // 위의 것과 같은 역할을 수행 (단, IArticleSub class의 Attach(IArticlePub* publisher) 함수에 의해서 불려짐 -> 조금의 파트만 Attach에서 고치면 된다) void NotifyDetach(IArticleSub* subscriber); // sub_list에서 구독자 삭제, IArticleSub에서 파라미터 받아옴 (이 때 구독자의 출판자 리스트 pub_list에도 동시에 삭제해 주어야 한다. AttachResponse(IArticlePub* p_pub) 참고하기 void NotifyDetachResponse(IArticleSub* subscriber); // 위의 것과 같은 역할을 수행 (단, IArticleSub class의 Attach(IArticlePub* publisher) 함수에 의해서 불려짐) void updatePubContents(string c); // c파라미터로 부터 contents 내용을 추가해 주고, 모든 구독자들에게 알림 (Notify() 활용하기) void Notify(); // 출판자에 의해 생성된 새로운 contents를 구독자에게 공지 (업데이트 될 때), IArticleSub class의 update 사용 고려하기 int getPubID(); // return pub_id string getPubName(); // return pub_name int getSubSize(); // return subscribers의 명 수 void PrintAllSub(); // 모든 구독자의 name과 id 출력, 출력 형식은 밑의 output 참고 }; class IArticleSub { private: string sub_name; // 구독자의 name 저장 (ex. Jenny, Tom ...) int sub_id; // 구독자 id 저장 (id는 출판자마다 unique 해야한다. 또한 n번째 구독자 id는 n이 되어야 한다) string recent_article_contents; // 출판자에 의해서 생성된 가장 최근의 contents 저장 -> update 함수에 의해서 업데이트 되고, 초기 상태는 empty IArticlePub* recent_article_pub; // 최근 contents를 공지한 출판자를 저장 -> update 함수에 의해서 업데이트 됨 IArticlePub* pub_list[5]; // 구독자가 구독하고 있는 출판자의 리스트 (이 문제에 구독할 수 있는 출판자는 최대 5명이라고 가정한다) int numOfPub = 0; // 현재 구독하고 있는 출판자의 명 수 저장 (초기 상태에서는 0명이다) static int static_sub_counter; // 현재까지 생성된 IArticleSub 객체의 갯수를 알려주는 static 변수 public: IArticleSub(string name); // constructor : 1. 파라미터로 sub 이름 초기화, 2.id 초기화 (static 변수를 이용해서 초기화 하면 될 것 같다), 3. output 보고 이에 맞는 정보 출력 IArticleSub(string name, IArticlePub* articlePub); // constructor : 위 1.,2.,3. 과정 똑같이 수행 + pub_list에 새로운 출판자 추가 sub_list에도 구독자 목록을 추가해야 함 ~IArticleSub(); // destructor : 1. delete 모든 pub_list의 item, 2. print (어떤 걸 print 해야하는 지는 IArticlePub의 DetachResponse 사용 고려하기) void Attach(IArticlePub* publisher); // pub_list에 새로운 출판자를 추가 -> sub_list에서도 추가가 되야함 (IArticlePub의 NotifyAttachResponse 사용 고려하기) void AttachResponse(IArticlePub* publisher); // 위의 역할과 같은 역할 수행 (단, 이것은 IArticlePub의 NotifyAttach에 의해서 불려짐 -> 조금의 파트만 Attach에서 고치면 된다) void Detach(IArticlePub* p_pub); // pub_list에 출판자를 삭제 -> sub_list에서도 삭제가 되야함 (IArticlePub의 NotifyDetachResponse 사용 고려하기) void DetachResponse(IArticlePub* p_pub); // 위와 같은 역할을 수행 (단, 이것은 IArticlePub의 NotifyDetach에 의해서 불려짐 -> 조금의 파트만 Detach에서 고치면 된다) void Update(IArticlePub* publisher, const string contents); // 업데이트 recent_article_contents, recent_article_pubs, (+ 이것은 PrintContents func을 부름, func은 업데이트 정보 출력해줌) 이 func은 IArticlePub의 Notify func에 의해 불리어짐 void PrintContents(); // 최신 정보 print (형식은 output 참고) string getSubName(); // 밑 4개 함수는 위 class와 동일 int getSubID(); int getPubSize(); void PrintAllPub(); }; int main() { IArticlePub* dgistPub = new IArticlePub("DGIST"); IArticlePub* bbcPub = new IArticlePub("BBC"); IArticlePub* cnnPub = new IArticlePub("CNN"); cout << endl; //Jenny subscribe DGIST, BBC IArticleSub* jennySub = new IArticleSub("Jenny", dgistPub); bbcPub->NotifyAttach(jennySub); cout << endl; //Tom subscribe BBC, CNN IArticleSub* tomSub = new IArticleSub("Tom"); bbcPub->NotifyAttach(tomSub); cnnPub->NotifyAttach(tomSub); cout << endl; //Kate subscribe DGIST, BBC, CNN IArticleSub* kateSub = new IArticleSub("Kate", dgistPub); bbcPub->NotifyAttach(kateSub); kateSub->Attach(cnnPub); cout << endl; cout << "All Sub of (" << dgistPub->getPubName() << "," << dgistPub->getPubID() << "): "; dgistPub->PrintAllSub(); cout << "All Sub of (" << bbcPub->getPubName() << "," << bbcPub->getPubID() << "): "; bbcPub->PrintAllSub(); cout << "All Sub of (" << cnnPub->getPubName() << "," << cnnPub->getPubID() << "): "; cnnPub->PrintAllSub(); cout << endl; kateSub->Detach(bbcPub); cout << endl; cout << "All Pub of (" << jennySub->getSubName() << "," << jennySub->getSubID() << "): "; jennySub->PrintAllPub(); cout << "All Pub of (" << tomSub->getSubName() << "," << tomSub->getSubID() << "): "; tomSub->PrintAllPub(); cout << "All Pub of (" << kateSub->getSubName() << "," << kateSub->getSubID() << "): "; kateSub->PrintAllPub(); cout << endl; cout << "=========DGIST Notify ===========" << endl; dgistPub->updatePubContents("Welcome New DGIST students"); cout << endl; cout << "=========BBC Notify ===========" << endl; bbcPub->updatePubContents("Mr. Son scored at Tottenham"); cout << endl; cout << "=========CNN Notify ===========" << endl; cnnPub->updatePubContents("New York city celebrates Christmas"); cout << endl; cout << "=========DELETING [tomSub]===========" << endl; delete tomSub; cout << endl; cout << "=========DGIST Notify ===========" << endl; dgistPub->updatePubContents("Welcome New DGIST students"); cout << endl; cout << "=========BBC Notify ===========" << endl; bbcPub->updatePubContents("Mr. Son scored at Tottenham"); cout << endl; cout << "=========CNN Notify ===========" << endl; cnnPub->updatePubContents("New York city celebrates Christmas"); cout << endl; cout << "=========Delete all others ===========" << endl; delete dgistPub; delete bbcPub; delete cnnPub; delete jennySub; delete kateSub; return 0; ---- [Constructor]New Pub Created: (DGIST,1) [Constructor]New Pub Created: (BBC,2) [Constructor]New Pub Created: (CNN,3) [Constructor]New Sub Created: (Jenny,1) [Attach] Pub (DGIST,1) is attached to Sub (Jenny,1) [Attach] Sub (Jenny,1) is attached to Pub (DGIST,1) [Attach] Sub (Jenny,1) is attached to Pub (BBC,2) [Attach] Pub (BBC,2) is attached to Sub (Jenny,1) [Constructor]New Sub Created: (Tom,2) [Attach] Sub (Tom,2) is attached to Pub (BBC,2) [Attach] Pub (BBC,2) is attached to Sub (Tom,2) [Attach] Sub (Tom,2) is attached to Pub (CNN,3) [Attach] Pub (CNN,3) is attached to Sub (Tom,2) [Constructor]New Sub Created: (Kate,3) [Attach] Pub (DGIST,1) is attached to Sub (Kate,3) [Attach] Sub (Kate,3) is attached to Pub (DGIST,1) [Attach] Sub (Kate,3) is attached to Pub (BBC,2) [Attach] Pub (BBC,2) is attached to Sub (Kate,3) [Attach] Pub (CNN,3) is attached to Sub (Kate,3) [Attach] Sub (Kate,3) is attached to Pub (CNN,3) All Sub of (DGIST,1): [Jenny,1][Kate,3] All Sub of (BBC,2): [Jenny,1][Tom,2][Kate,3] All Sub of (CNN,3): [Tom,2][Kate,3] [Sub] (Kate,3) unsubscribes [Pub] (BBC,2) [Pub] (BBC,2) detach [Sub] (Kate,3) All Pub of (Jenny,1): [DGIST,1][BBC,2] All Pub of (Tom,2): [BBC,2][CNN,3] All Pub of (Kate,3): [DGIST,1][CNN,3] =========DGIST Notify =========== Sub (Jenny,1)'s latest subscribed news is "Welcome New DGIST students" by DGIST Sub (Kate,3)'s latest subscribed news is "Welcome New DGIST students" by DGIST =========BBC Notify =========== Sub (Jenny,1)'s latest subscribed news is "Mr. Son scored at Tottenham" by BBC Sub (Tom,2)'s latest subscribed news is "Mr. Son scored at Tottenham" by BBC =========CNN Notify =========== Sub (Tom,2)'s latest subscribed news is "New York city celebrates Christmas" by CNN Sub (Kate,3)'s latest subscribed news is "New York city celebrates Christmas" by CNN =========DELETING [tomSub]=========== IArticleSub Destructor called [Pub] (BBC,2) detach [Sub] (Tom,2) [Pub] (CNN,3) detach [Sub] (Tom,2) =========DGIST Notify =========== Sub (Jenny,1)'s latest subscribed news is "Welcome New DGIST students" by DGIST Sub (Kate,3)'s latest subscribed news is "Welcome New DGIST students" by DGIST =========BBC Notify =========== Sub (Jenny,1)'s latest subscribed news is "Mr. Son scored at Tottenham" by BBC =========CNN Notify =========== Sub (Kate,3)'s latest subscribed news is "New York city celebrates Christmas" by CNN =========Delete all others =========== IArticlePub Destructor called [Sub] (Jenny,1) unsubscribes [Pub] (DGIST,1) [Sub] (Kate,3) unsubscribes [Pub] (DGIST,1) IArticlePub Destructor called [Sub] (Jenny,1) unsubscribes [Pub] (BBC,2) IArticlePub Destructor called [Sub] (Kate,3) unsubscribes [Pub] (CNN,3) IArticleSub Destructor called IArticleSub Destructor called
코드 작성
#include <iostream> using namespace std; class IArticlePub; class IArticleSub; class IArticlePub { private: string pub_name; int pub_id; string recent_contents; IArticleSub* sub_list[5]; int numOfSub = 0; static int static_pub_counter; public: IArticlePub(const string name); IArticlePub(const string name, const string con); ~IArticlePub(); void NotifyAttach(IArticleSub* subscriber); void NotifyAttachResponse(IArticleSub* subscriber); void NotifyDetach(IArticleSub* subscriber); void NotifyDetachResponse(IArticleSub* subscriber); void updatePubContents(string c); void Notify(); int getPubID() { return pub_id; } string getPubName() { return pub_name; } int getSubSize(); void PrintAllSub(); }; class IArticleSub { private: string sub_name; int sub_id; string recent_article_contents; IArticlePub* recent_article_pub; IArticlePub* pub_list[5]; int numOfPub = 0; static int static_sub_counter; public: IArticleSub(string name); IArticleSub(string name, IArticlePub* articlePub); ~IArticleSub(); void Attach(IArticlePub* publisher); void AttachResponse(IArticlePub* publisher); void Detach(IArticlePub* p_pub); void DetachResponse(IArticlePub* p_pub); void Update(IArticlePub* publisher, const string contents); void PrintContents(); string getSubName() { return sub_name; } int getSubID() { return sub_id; } int getPubSize(); void PrintAllPub(); }; // IArticlePub's implements -------------------------------------------------------------------------------------------------- int IArticlePub::static_pub_counter = 1; IArticlePub::IArticlePub(const string name) { pub_name = name; pub_id = static_pub_counter++; cout << "[Constructor]New Pub Created: " << "(" << pub_name << "," << pub_id << ")" << endl; } IArticlePub::IArticlePub(const string name, const string con) { pub_name = name; pub_id = static_pub_counter++; recent_contents = con; cout << "[Constructor]New Pub Created: " << "(" << pub_name << "," << pub_id << ")" << endl; } IArticlePub::~IArticlePub() { cout << "IArticlePub Destructor called" << endl; for (int i = 0; i < numOfSub; i++) { while (i < 5) { if (sub_list[i] != nullptr) { sub_list[i]->DetachResponse(this); break; } else { i++; } } } } void IArticlePub::NotifyAttach(IArticleSub* subscriber) { int N = 0; for (int i = 0; i < numOfSub; i++) { while (i < 5) { if (sub_list[i] == subscriber) { cout << "Error Message" << endl; N = 1; break; } else { i++; } } } if (N == 0) { sub_list[numOfSub] = subscriber; numOfSub++; subscriber->AttachResponse(this); cout << "[Attach] Pub " << "(" << pub_name << "," << pub_id << ")"; cout << " is attached to Sub " << "(" << subscriber->getSubName() << "," << subscriber->getSubID() << ")" << endl; } } void IArticlePub::NotifyAttachResponse(IArticleSub* subscriber) { int N = 0; for (int i = 0; i < numOfSub; i++) { while (i < 5) { if (sub_list[i] == subscriber) { cout << "Error Message" << endl; N = 1; break; } else { i++; } } } if (N == 0) { sub_list[numOfSub] = subscriber; numOfSub++; cout << "[Attach] Pub " << "(" << pub_name << "," << pub_id << ")"; cout << " is attached to Sub " << "(" << subscriber->getSubName() << "," << subscriber->getSubID() << ")" << endl; } } void IArticlePub::NotifyDetach(IArticleSub* subscriber) { int N = 0; for (int i = 0; i < numOfSub; i++) { while (i < 5) { if (sub_list[i] == subscriber) { sub_list[i] = nullptr; numOfSub--; cout << "[Pub] (" << pub_name << "," << pub_id << ") detach [Sub] (" << subscriber->getSubName(); cout << "," << subscriber->getSubID() << ")" << endl; subscriber->DetachResponse(this); N = 1; break; } else { i++; } } } if (N == 0) { cout << "Error Message" << endl; } } void IArticlePub::NotifyDetachResponse(IArticleSub* subscriber) { int N = 0; for (int i = 0; i < numOfSub; i++) { while (i < 5) { if (sub_list[i] == subscriber) { sub_list[i] = nullptr; numOfSub--; cout << "[Pub] (" << pub_name << "," << pub_id << ") detach [Sub] (" << subscriber->getSubName(); cout << "," << subscriber->getSubID() << ")" << endl; N = 1; break; } else { i++; } } } if (N == 0) { cout << "Error Message" << endl; } } void IArticlePub::updatePubContents(string c) { recent_contents = c; Notify(); } void IArticlePub::Notify() { for (int i = 0; i < numOfSub; i++) { while (i < 5) { if (sub_list[i] != nullptr) { sub_list[i]->Update(this, recent_contents); cout << endl; break; } else { i++; } } } cout << endl; } int IArticlePub::getSubSize() { int size = numOfSub; return size; } void IArticlePub::PrintAllSub() { for (int i = 0; i < numOfSub; i++) { while (i < 5) { if (sub_list[i] != nullptr) { cout << "[" << sub_list[i]->getSubName() << "," << sub_list[i]->getSubID() << "]"; break; } else { i++; } } } cout << endl; } // IArticleSub's implements -------------------------------------------------------------------------------------------------- int IArticleSub::static_sub_counter = 1; IArticleSub::IArticleSub(const string name) { sub_name = name; sub_id = static_sub_counter++; cout << "[Constructor]New Sub Created: " << "(" << sub_name << "," << sub_id << ")" << endl; } IArticleSub::IArticleSub(string name, IArticlePub* articlePub) { sub_name = name; sub_id = static_sub_counter++; cout << "[Constructor]New Sub Created: " << "(" << sub_name << "," << sub_id << ")" << endl; Attach(articlePub); } IArticleSub::~IArticleSub() { cout << "IArticleSub Destructor called" << endl; for (int i = 0; i < numOfPub; i++) { while (i < 5) { if (pub_list[i] != nullptr) { pub_list[i]->NotifyDetachResponse(this); break; } else { i++; } } } } void IArticleSub::Attach(IArticlePub* publisher) { int N = 0; for (int i = 0; i < numOfPub; i++) { while (i < 5) { if (pub_list[i] == publisher) { cout << "Error Message" << endl; N = 1; break; } else { i++; } } } if (N == 0) { pub_list[numOfPub] = publisher; numOfPub++; publisher->NotifyAttachResponse(this); cout << "[Attach] Sub " << "(" << sub_name << "," << sub_id << ")"; cout << " is attached to Pub " << "(" << publisher->getPubName() << "," << publisher->getPubID() << ")" << endl; } } void IArticleSub::AttachResponse(IArticlePub* publisher) { int N = 0; for (int i = 0; i < numOfPub; i++) { while (i < 5) { if (pub_list[i] == publisher) { cout << "Error Message" << endl; N = 1; break; } else { i++; } } } if (N == 0) { pub_list[numOfPub] = publisher; numOfPub++; cout << "[Attach] Sub " << "(" << sub_name << "," << sub_id << ")"; cout << " is attached to Pub " << "(" << publisher->getPubName() << "," << publisher->getPubID() << ")" << endl; } } void IArticleSub::Detach(IArticlePub* p_pub) { int N = 0; for (int i = 0; i < numOfPub; i++) { while (i < 5) { if (pub_list[i] == p_pub) { pub_list[i] = nullptr; numOfPub--; cout << "[Sub] (" << sub_name << "," << sub_id << ") unsubscribes [Pub] (" << p_pub->getPubName(); cout << "," << p_pub->getPubID() << ")" << endl; p_pub->NotifyDetachResponse(this); N = 1; break; } else { i++; } } } if (N == 0) { cout << "Error Message" << endl; } } void IArticleSub::DetachResponse(IArticlePub* p_pub) { int N = 0; for (int i = 0; i < numOfPub; i++) { while (i < 5) { if (pub_list[i] == p_pub) { pub_list[i] = nullptr; numOfPub--; cout << "[Sub] (" << sub_name << "," << sub_id << ") unsubscribes [Pub] (" << p_pub->getPubName(); cout << "," << p_pub->getPubID() << ")" << endl; N = 1; break; } else { i++; } } } if (N == 0) { cout << "Error Message" << endl; } } void IArticleSub::Update(IArticlePub* publisher, const string contents) { recent_article_pub = publisher; recent_article_contents = contents; PrintContents(); } void IArticleSub::PrintContents() { cout << "Sub" << " (" << sub_name << "," << sub_id << ")"; cout << "'s lastest subscribed news is " << '"' << recent_article_contents; cout << '"' << " by " << recent_article_pub->getPubName() << endl; } int IArticleSub::getPubSize() { int size = numOfPub; return size; } void IArticleSub::PrintAllPub() { for (int i = 0; i < numOfPub; i++) { while (i < 5) { if (pub_list[i] != nullptr) { cout << "[" << pub_list[i]->getPubName() << "," << pub_list[i]->getPubID() << "]"; break; } else { i++; } } } cout << endl; } int main() { IArticlePub* dgistPub = new IArticlePub("DGIST"); IArticlePub* bbcPub = new IArticlePub("BBC"); IArticlePub* cnnPub = new IArticlePub("CNN"); cout << endl; //Jenny subscribe DGIST, BBC IArticleSub* jennySub = new IArticleSub("Jenny", dgistPub); bbcPub->NotifyAttach(jennySub); cout << endl; //Tom subscribe BBC, CNN IArticleSub* tomSub = new IArticleSub("Tom"); bbcPub->NotifyAttach(tomSub); cnnPub->NotifyAttach(tomSub); cout << endl; //Kate subscribe DGIST, BBC, CNN IArticleSub* kateSub = new IArticleSub("Kate", dgistPub); bbcPub->NotifyAttach(kateSub); kateSub->Attach(cnnPub); cout << endl; cout << "All Sub of (" << dgistPub->getPubName() << "," << dgistPub->getPubID() << "): "; dgistPub->PrintAllSub(); cout << "All Sub of (" << bbcPub->getPubName() << "," << bbcPub->getPubID() << "): "; bbcPub->PrintAllSub(); cout << "All Sub of (" << cnnPub->getPubName() << "," << cnnPub->getPubID() << "): "; cnnPub->PrintAllSub(); cout << endl; kateSub->Detach(bbcPub); cout << endl; cout << "All Pub of (" << jennySub->getSubName() << "," << jennySub->getSubID() << "): "; jennySub->PrintAllPub(); cout << "All Pub of (" << tomSub->getSubName() << "," << tomSub->getSubID() << "): "; tomSub->PrintAllPub(); cout << "All Pub of (" << kateSub->getSubName() << "," << kateSub->getSubID() << "): "; kateSub->PrintAllPub(); cout << endl; cout << "=========DGIST Notify ===========" << endl; dgistPub->updatePubContents("Welcome New DGIST students"); cout << endl; cout << "=========BBC Notify ===========" << endl; bbcPub->updatePubContents("Mr. Son scored at Tottenham"); cout << endl; cout << "=========CNN Notify ===========" << endl; cnnPub->updatePubContents("New York city celebrates Christmas"); cout << endl; cout << "=========DELETING [tomSub]===========" << endl; delete tomSub; cout << endl; cout << "=========DGIST Notify ===========" << endl; dgistPub->updatePubContents("Welcome New DGIST students"); cout << endl; cout << "=========BBC Notify ===========" << endl; bbcPub->updatePubContents("Mr. Son scored at Tottenham"); cout << endl; cout << "=========CNN Notify ===========" << endl; cnnPub->updatePubContents("New York city celebrates Christmas"); cout << endl; cout << "=========Delete all others ===========" << endl; delete dgistPub; delete bbcPub; delete cnnPub; delete jennySub; delete kateSub; return 0; }
과제 주의 사항 및 메모
- 객체 그 자신은 this로 가르키면 된다.
- 함수 불러올 때는 괄호까지 모두 불러와야 한다.
02. Text assignment

Ahyeon
notes
codes
0904_lec 3
#include <iostream> using namespace std; int main() { /* int iVal1 = 3; int iVal2 = iVal1++; // postfix cout << iVal1 << "\t" << iVal2 << endl; iVal1 = 3; iVal2 = ++iVal1; //prefix cout << iVal1 << "\t" << iVal2 << endl; int iVal = 1; cout << iVal << (iVal << 1) << endl; cout << (iVal << 2) << (iVal << 3) << endl; int x = 1, y = 2; cout << (++x > 2 && ++y < 2) << endl; cout << "x=" << x << ", y=" << y << endl; cout << (++x < 2 || ++y > 2) << endl; cout << "x=" << x << ", y=" << y << endl; //and or operator precedence를 잘 이해하자! */ //lec3-FlowControl /* //Multiple branches int iScore{ 0 }; cout << "Enter your score: " << endl; cin >> iScore; if (iScore >= 85) { cout << iScore; cout << "Points:"; cout << "\t"; cout << "Grade A"; cout << endl; } else if (iScore >= 70) cout << iScore << "Points:\tGrade B\n"; else if (iScore >= 50) cout << iScore << "Points:\tGrade C\n"; else cout << iScore << "Points:\tGrade F\n"; //Ternary Operator(삼항연산자) int max{ 0 }; int x{ 10 }, y{ 20 }; if (x > y) { max = x; } else { max = y; } cout << "max1: "<< max<<endl; int max2{ 0 }; int x2{ 10 }, y2{ 20 }; max2 = (x2 > y2) ? x2 : y2; cout << "max2: "<< max2; */ /* //Switch E.g. char grade{ 'F' }; cin >> grade; switch (grade) { case 'A': case 'a': cout << "85~100" << endl; break; case 'B': case 'b': cout << "70~84" << endl; break; case 'C': case 'c': cout << "50~69" << endl; break; default: cout << "less than 50" << endl; } */ int sum{ 0 }; for (int i = 0; i < 4; i++) sum += i; cout << sum << endl; return 0; }
0906_lec 4
#include <iostream> using namespace std; #define PI 3.14 string order(int menu) { if (menu == 1) return "sold out"; else return "successfully ordered"; } //function declaration double calArea(int radius); int main() { /* break 과 continue for (int i = 1; i < 11; i++) { if (i == 4) break; cout << i << "\t"; } cout << endl; for (int i = 1; i < 11; i++) { if (i % 2 == 0) continue; cout << i << "\t"; } */ /* //Circumference Calculator int iValue(2); double circum{ 0 }; for (;;) { cout << "Enter the radius? "; cin >> iValue; if (iValue == 0) break; circum = iValue * 2 * PI; cout << "circumference with a radius of " << iValue << " : "; cout << circum << "(" << typeid(circum).name() << ")" << endl; } */ /* int arr[5] = { 1,3,5,7,9 }; cout << sizeof(arr) << endl; cout << sizeof(int) << endl; for (int i = 0; i < sizeof(arr) / sizeof(int); i++) { int element = arr[i]; cout << element << "\t"; } return 0; */ /* int iMenu{ 1 }; cout << "1. Coffee\n2. Juice\n3. Quit\n"; do { cout << "Select Menu?"; cin >> iMenu; cout << order(iMenu) << endl; } while (iMenu != 3); return 0; */ int iVal(0); double dVal{ 0 }; cout << "Enter the radius? "; cin >> iVal; dVal = calArea(iVal); cout << dVal << endl; return 0; } //Function implementation double calArea(int radius) { double dVal; dVal = radius * radius * 3.14; return dVal; }
0918_lec 6
#include <iostream> #include <string> using namespace std; /* int main() { /* review Test */ /* int a = 3; int* p_a = &a; int** pp_a = &p_a; float b = 20.5; float* p_b = &b; cout << "p_a: " << p_a << endl; //Address cout << "pp_a: " << pp_a << endl; //Address cout << "p_b: " << p_b << endl; cout << "*p_a: " << *p_a << endl; //Dereferencing cout << "*pp_a: " << *pp_a << endl; //Dereferencing p_a의 주소와 동일 cout << "*p_b: " << *p_b << endl; */ /* int iNum = 0; int iNums[3] = { 1, 2, 3 }; int* pNum = &iNum; int* pNums1 = &iNums[0]; int* pNums2 = iNums; cout << pNums1 << endl << pNums2 << endl; cout << *pNums1 << endl << *pNums2 << endl; cout << iNums[1] << endl << pNums1[1] << endl << pNums2[1] << endl; */ /* int iNum[3] = { 1, 2, 3 }; int* pNum = iNum; cout << pNum << endl; cout << pNum + 1 << endl; cout << pNum + 2 << endl; cout << *pNum << endl; cout << *(pNum + 1) << endl; *//* int iNums[2][3] = { 1,2,3,4,5,7 }; cout << iNums << endl; cout << *iNums << endl; cout << **iNums << endl; cout << **(iNums + 1) << endl; cout << *((*iNums) + 1) << endl; cout << *((*(iNums + 1)) + 1) << endl; cout << *((*(iNums + 1)) + 1) + 1 << endl; cout << "iNums[0]: " << iNums[0] << endl; cout << "iNums[1]: " << iNums[1] << endl; cout << "iNums[0][1]: " << iNums[0][1] << endl; */ /* lecture 6 강의자료 시작 */ /* double arr[3]; double* ptr; cout << "Displaying address using arrays: " << endl; for (int i = 0; i < 3; ++i) { cout << "&arr[" << i << "]=" << &arr[i] << endl; } ptr = arr; cout << "\nDisplaying address using pointers: " << endl; for (int i = 0; i < 3; ++i) { cout << "ptr+" << i << "=" << ptr + i << endl; } */ /* } } */ /* void fun2(int arr[], unsigned int n) { int i; for (i = 0; i < n; i++) cout << arr[i] << endl; } int main() { int arr[] = { 1,2,3,4,5,6,7,8 }; unsigned int n = sizeof(arr) / sizeof(arr[0]); cout << "Array size inside main() is : " << n << endl; fun2(arr, n); return 0; } */ int main() { int* ptr; ptr = new int; //Heap allocation *ptr = 123; cout << "Value of *ptr: " << *ptr << endl; delete ptr; int x = 10; int* ptr1 = &x; delete ptr1; // Runtime Error; delete는 heap 명령에 존재하는 메모리 컨트롤 위한것, x와 ptr1은 stack 명령에 잡힌 메모리 공간, stack 영역의 포인터를 heap에서 사용되는 delete operation 사용하려 하니까 compile에서는 잡히진 않지만 프로그램 수행시 두 메모리의 차이 때문에 에러가 난다~! return 0; }
0920_lec 6
#include <iostream> using namespace std; /* int main() { /*p. 11 int* ptr; ptr = new int[10]; ptr[0] = 0; *(ptr + 1) = 1; for (int i = 0; i < 10; i++) *(ptr + i) = i; for (int i = 0; i < 10; i++) cout << ptr[i] << " "; delete[] ptr; */ /* // Heap이 계속 성장한다면? deallocation을 하지 않는다면?? // 끝나지 않아야하는데,, termination이 있음.. 왜지? // heap에 많은 data를 allocation하면서 stack 쪽으로 iteration마다 메모리 증가 // 프로그램은 제한된 메모리, stack의 main함수 영역과 heap에 Hello 찍는게 만남 // 내부적으로 컴파일러가 종료시킴. int* ptr; while (1) { cout << " Hello"; ptr = new int[100000]; // Heal allocation //delete[] ptr; //이거 추가하면 메모리가 늘었다 줄었다 반복하면서 무한 반복,,, } return 0; */ /* //Memory Leak int* ptr = new int{ 10 }; int* ptr2 = new int{ 20 }; *ptr = 30; ptr = ptr2; cout << ptr << " " << ptr2 << endl; cout << *ptr << " " << *ptr2 << endl; //Dangling pinter int* ptr3 = new int{ 40 }; // heap allocation cout << "Do something BEFORE delete" << endl; delete ptr3; // heap deallocation *ptr3 = 10; // Acess dangling pointer cout << "Do something AFTER delete" << endl; // 이거 실행 안됨,, 이 전 줄에서 프로그램 비정상 종료 return 0; */ /* //Reference Type // iNum의 별명을 rNum으로 만들어보자~ int iNum = 10; int& rNum = iNum; //Reference int* pNum = &iNum; //Pointer // 위에 3개 다 같은 메모리 주소 가리킴. cout << "value: " << iNum << " address: " << &iNum << endl; cout << "value: " << rNum << " address: " << &rNum << endl; cout << "value: " << *pNum << " address: " << pNum << endl; cout << endl; rNum = 5; cout << "value: " << iNum << " address: " << &iNum << endl; cout << "value: " << rNum << " address: " << &rNum << endl; cout << "value: " << *pNum << " address: " << pNum << endl; cout << endl; *pNum = 50; cout << "value: " << iNum << " address: " << &iNum << endl; cout << "value: " << rNum << " address: " << &rNum << endl; cout << "value: " << *pNum << " address: " << pNum << endl; int a = 10; int* p; int** pp; // double pointer p = &a; pp = &p; return 0; *//* } */ /* void intSwap1(int num1, int num2) { int temp{ num1 }; num1 = num2; num2 = temp; } void intSwap2(int* num1, int* num2) { int temp{ *num1 }; *num1 = *num2; *num2 = temp; } void intSwap3(int& num1, int& num2) { int temp{ num1 }; num1 = num2; num2 = temp; } int* f1() { int iNums[3]{ 1,2,3 }; // allocated in stack return iNums; } int* f2() { int* iNums = new int[3]; // heap allocation iNums[0] = 1; iNums[1] = 2; iNums[2] = 3; return iNums; } void f3(int arr[], int size) { // 전에는 main에서 arr을 만들고 f3에서 control하는 느낌였다면 f1, f2는 반대! for (int i = 0; i < size; i++) { arr[i] = arr[i] + 10; } } int main() { int iNum1{ 1 }; int iNum2{ 3 }; cout << iNum1 << "" << iNum2 << endl; intSwap3(iNum1, iNum2); cout << iNum1 << "" << iNum2 << endl; //int* pNums = f1(); int* pNums = f2(); cout << *pNums << endl; cout << *(pNums + 1) << endl; // 얘랑 밑에 애는 제대로 안나와 cout << *(pNums + 2) << endl; // main영역에 pNums, f1영역에 iNums[0], [1], [2] 할당 될 것. // 근데 stack에서 f1이 return되는 시점에 컴파일러에 의해 deallocation됨 주소만 받아온거라 사용되지 않는 공간이라 접근 불가능.. //heap 쓸거면 deallocation해야지 delete[] pNums; return 0; } */ /* int foo() { return 5; } double goo() { return 6; } int hoo(int n) { return n; } int main() { int (*fcnPtr)() { &foo }; double(*dfcnPtr)(); int(*pfcnPtr)(int); dfcnPtr = &goo; pfcnPtr = &hoo; cout << fcnPtr() << endl; cout << fcnPtr() << endl; cout << pfcnPtr(8) << endl; return 0; } */ int main() { //const int value{ 5 }; // More restrictive. int value{ 5 }; int* ptr{ &value }; *ptr = 6; cout << "value: " << value << endl << "*ptr: " << *ptr << endl; }
0925_lec 7
#include <iostream> #include <string.h> using namespace std; /* struct Student { int id; string name; Student* friends; //pointer }; */ class Student { public: int id; string name; }; int main() { /* //class(1) page 6 Student students[10]; Student* student1 = new Student{ 201911999, "John" , nullptr}; Student* student2 = new Student{ 5678, "Tom", student1 }; student1->friends = student2; students[0] = (*student1);//dereferencing을 통해서 copy 하기 때문에 각 데이터는 별도의 메모리 공간에 존재하기 때문에 students[1] = (*student2); cout << students[0].name << endl; cout << students[1].name << endl; //cout << &students[0] << endl; //이거랑 //cout << student1 << endl; // 얘랑 주소가 다릅니다! cout << students[0].friends << endl; // 00B29486 cout << student1->friends << endl; // 00B29486 cout << student2 << endl; // 00B29486 //cout << student1->friends->name; /*cout << students[0].id << endl; cout << (*student1).id << endl; cout << student1->id << endl; cout << sizeof(int) << endl; cout << sizeof(Student) << endl; delete student1; delete student2; return 0; */ Student* student1; student1 = new Student{ 1234, "John" }; cout << (*student1).id << endl; cout << student1->id << endl; return 0; }
0927_lec 7
#include <iostream> #include <string> using namespace std; class Student { private: int* m_pID; string m_name; public: Student(); // Constructor (without parameter) Student(int, string); // Constructor (with parameter) ~Student(); // Destructor int GetID() { return *m_pID; } string GetName() { return m_name; } }; void Student::InitVariables(int, string name) { m_pID = new int(0); m_name = "Alice"; } //Constructor implementation (ver 1) Student::Student() { m_pID = new int(0); m_name = "Alice"; cout << "Constructor w/o parameter: " << m_name << endl; } //Constructor implementation (ver 2) Student::Student(int id, string name) { m_pID = new int(id); m_name = name; cout << "Constructor with parameter: " << m_name << endl; } //Destructor implementation Student::~Student() { cout << "Destructor: " << m_name << endl; delete m_pID; } int main() { Student s1; s1.InitVariables(123, "Alice"); Student s2; s2.InitVariables(456, "Tom"); cout << s1.GetID() << " " << s1.GetName() << endl; cout << s2.GetID() << " " << s2.GetName() << endl; /* cout << "Place 1" << endl; Student s1; Student s2(2, "Jenny"); cout << "Place 2" << endl; Student* s3; cout << "Place 3" << endl; s3 = new Student(4, "Tom"); cout << "Place 4" << endl; delete s3; cout << "Place 5" << endl; */ /* cout << "Place 1" << endl; Student s1; //Stack allocation cout << "Place 2" << endl; // 이후 main 함수가 return 될 때 스택에 allocation 되어있던거 deallocation되면서 destructor 자동으로 불려짐 */ /* cout << "Place 1" << endl; Student* s1; cout << "Place 2" << endl; s1 = new Student(123, "John"); cout << "Place 3" << endl; delete s1; // Heap deallocation */ /* cout << "Place 1" << endl; //Student s1(123, "Tom"); Student* s1; // Student형 포인터 타입을 설정 -> 실제로 Student형은 아니다, 자리만 만든거// Pointer at Stack cout << "Place 2" << endl; //cout << s1.GetID() << " " << s1.GetName() << endl; Student* s2; // Pointer at Stack cout << "Place 3" << endl; s1 = new Student(123, "John"); //heap allocation(ver 2) cout << "Place 4" << endl; s2 = new Student(); // Heap allocation(ver 1) cout << "Place 5" << endl; Student s3; // Stack allocation cout << "Place 6" << endl; Student s4(456, "Jenny"); cout << "Place 7" << endl; Student many_stdents[3]; // stack allocation cout << "Place 8" << endl; */ return 0; }
1004_lec 7
// lecture 7 ppt 19p. /* #include <iostream> #include <string> using namespace std; class Student{ private: int* m_pId; string m_name; public: Student(); //Constructor 1 Student(int, string); //Constructor 2 ~Student(); //Destructor int GetId() { return *m_pId; } void SetID(int); }; void Student::SetID(int ID) { //*m_pId = ID; //이런방법도 있고 *(this->m_pId) = ID; } //Constructor 1 implementation Student::Student() : Student(0, "") { cout << "Constructor 1" << m_name << endl; } //Constructor 2 implementation Student::Student(int id, string name) { m_pId = new int{ id }; m_name = name; cout << "Constructor 2" << m_name << endl; } //Destructor implementation Student::~Student() { if (m_pId != nullptr) { delete m_pId; m_pId = nullptr; } cout << "Destructor " << m_name << endl; } int main() { Student s1; //Stack allocation Student* s2; //Pointer s2 = new Student(123, "Jenny"); //Heap allocation cout << s2->GetId() << endl; s2->SetID(456); //update cout << s2->GetId() << endl; delete s2; //Heap deallocation return 0; } */ //lecture 8 //이거 파일 분리하는 건데,, 솔루션 탐색기(ctrl+alt+L)에서 하면 됨 // 소스파일에는 cpp파일들 헤더파일에는 h 파일들 새로 만들고 ppt에 있는 거 하심 되여
1011_lec 8_class_하는 중…귀찮,,
/* main.cpp */ #include <iostream> #include "Student.h" #include "Teacher.h" #include "Lecture.h" using namespace std; int main() { Student s1(201911999, "Alice"); Teacher t1(111111111, "Tom"); Lecture l1(222222222, "OOP"); cout << s1.GetName() << endl; cout << t1.GetName() << endl; cout << l1.GetName() << endl; return 0; }
1023_lec 9
/* lec 9 p.9 */ #include <iostream> #include <string> using namespace std; //p.11 Teacher class추가 class Teacher { private: int* m_pId; string m_name; string teacher_hobby = "Play a Video Game"; string GetTeacherHobby(Teacher t) { return t.teacher_hobby; }; //version 2 //friend string GetTeacherHobby(Teacher t) { //version 1 // return t.teacher_hobby; //}; }; class Student { private: int* m_pId = nullptr; string m_name = ""; public: static int StudentCnt; //static variable Student(int, string); //Constructor Student(const Student& rhs); //Copy Constructor ~Student(); //Destructor int Get_ID() { return *(this->m_pId); } int* Get_ID_Addr() { return this->m_pId; } string Get_Name() const { return this->m_name; } // const function void Set_Name(string newName) { this->m_name = newName; } //여기에 const를 추가하면 Get_Name에서 만든 것과 충돌해 compile error //void ShowTeacherHobby(Teacher t) { cout << GetTeacherHobby(t) << endl; } //version 1 /*void ShowTeacherHobby(Teacher t) { // version 2 이부분만 고치면 되는데 그 위에 있는 방식이랑 똑같이 할 수 있단 걸 보여주시려 하셨음 cout << t.teacher_hobby << endl; cout << t.GetTeacherHobby(t); }*/ }; Student::Student(int id, string name) : m_pId{ new int{id} }, m_name{ name }{} Student::~Student() { delete m_pId; } Student::Student(const Student& rhs) { this->m_pId = new int(*rhs.m_pId); this->m_name = rhs.m_name; } int Student::StudentCnt = 0; //p.14 class Complex { int* m_r = nullptr; int* m_i = nullptr; public: Complex(); Complex(int, int); Complex(int); ~Complex(); Complex(const Complex& rhs); void print() const; Complex operator+(const Complex& c2); Complex operator+(int r); Complex operator+(double r); friend Complex operator+(double r, const Complex& c); //lhs가 double type //p.20부턴 다음시간에~ }; Complex Complex::operator+(const Complex& c2) { Complex result; *(result.m_r) = *(m_r)+*(c2.m_r); *(result.m_i) = *(m_i)+*(c2.m_i); return result; } Complex Complex::operator+(int r) { Complex result; *(result.m_r) = *(m_r)+r; *(result.m_i) = *(m_i); return result; } Complex Complex::operator+(double r) { return Complex((static_cast<int>(r)) + *(m_r), *(m_i)); } Complex operator+(double r, const Complex& c) { Complex result((static_cast<int> (r)) + *(c.m_r), * (c.m_i)); return result; } Complex::Complex(int r, int i) { m_r = new int(r); m_i = new int(i); } Complex::~Complex() { if (m_r) delete m_r; if (m_i) delete m_i; m_r = m_i = nullptr; } Complex::Complex() :Complex(0, 0) {} Complex::Complex(int r) : m_r{ new int(r) }, m_i{ new int(0) }{} Complex::Complex(const Complex& rhs) : Complex(*rhs.m_r, *rhs.m_i) {} void Complex::print() const { cout << *m_r << (*m_i < 0 ? "" : "+") << *m_i << "j" << endl; } int main() { Complex c1; Complex c2(3, 1234); Complex c3(-3); Complex c4(c2); c1.print(); c2.print(); c3.print(); c4.print(); Complex c5(c2 + c3); c5.print(); Complex c6(c2 + 4); c6.print(); Complex c7(c2 + 5.2); c7.print(); Complex c8(8.3 + c2); c8.print(); return 0; /* Student s1(201911999, "Alice"); Student::StudentCnt++; Student s2(s1); Student::StudentCnt++; Teacher t1; //cout << GetTeacherHobby(t1) << endl; s1.ShowTeacherHobby(t1); cout << "s1: " << s1.Get_Name() << " " << s1.Get_ID() << " " << s1.Get_ID_Addr() << endl; cout << "s2: " << s2.Get_Name() << " " << s2.Get_ID() << " " << s2.Get_ID_Addr() << endl; cout << "Total student: " << Student::StudentCnt << endl; return 0; */ }
Soomin
codes
lecture 3_1
#include <iostream> using namespace std; int main(){ /* Conditional Statement – Ternary Operator (condition) ? Statement (if true) : Statement (if false) */ int max{ 0 }; int x{ 10 }, y{ 20 }; max = (x > y) ? x : y; // 아래 if-else문이랑 같음 /* if (x>y){ max = x; } else{ max = y; } */ cout << max << endl << endl; /* Conditional Statement – Switch switch (variable or expression){ case constant1 : statements; break; case constant2 : statements; ... default : statements; } */ char grade{ 'F' }; cout << "Enter Grade: "; cin >> grade; switch (grade) { case 'A': case 'a': cout << "85~100" << endl; break; case 'B': case 'b': cout << "70~84" << endl; break; case 'C': case 'c': cout << "50~69" << endl; break; default: cout << "less than 50" << endl; } // 아래 if-else문과 같음 /* if (grade == 'A' || grade == 'a') cout << "85~100" << endl; else if (grade == 'B' || grade == 'b') cout << "70~84" << endl; else if (grade == 'C' || grade == 'c') cout << "50~69" << endl; else cout << "less than 50" << endl; */ cout << endl; // for loop int sum{ 0 }; for(int i=0;i < 11;i++){ sum +=i; // i = 0, 1, 2, 3 cout << sum << "\t"; // sum = 0, 1, 3, 6 } cout << endl; // goto label int a{0}, sum0{ 0 }; Sum: sum0 += a; a++; if (a < 11) goto Sum; else cout << sum0 << endl; cout << endl << endl; // break : escaping from the closest loop block for (int j = 1; j < 11; j++) { // j = 1, 2, 3 (4 이후에는 break) if (j == 4) break; cout << j << "\t"; } cout << endl << endl; // continue : ignoring the rest of loop block for (int k = 1; k < 11; k++) { // k = 1, 3, 5, 7, 9 (짝수일 때에는 continue) if (k % 2 == 0) continue; cout << k << "\t"; } return 0; }
lecture 3_2
#include <iostream> #define PI 3.14 using namespace std; int main(){ int iValue(2); double circum{0}; for (;;) { // while문과 동일한 기능?? cout << "Enter the radius? "; cin >> iValue; if (iValue == 0) break; circum = iValue * 2 * PI; cout << "circumference with a radius of " << iValue << " : "; cout << circum << "(" << typeid(circum).name() << ")" << endl; } cout << endl; /* Loop Statement – Range-based For loop for ( ① : ② ) statement; ① individual element of ② ② array or container */ int arr[5] = { 1,3,5,7,9 }; for (int element : arr){ cout << element << " "; } cout << endl << endl; /* Loop Statement – While loop while (condition) {Statements} */ int iMenu{ 1 }; cout << "1. Coffee\n2. Juice\n3. Quit\n"; cout << "Select Menu? "; cin >> iMenu; cout << "Your choice is " << iMenu << endl; while (iMenu != 3){ cout << "Select Menu? "; cin >> iMenu; cout << "Your choice is " << iMenu << endl; } /* Loop Statement – Do ~ While loop do {Statements} while (condition); */ int iChoice{ 1 }; cout << "1. Coffee\n2. Juice\n3. Quit\n"; do{ cout << "Select Choice? "; cin >> iChoice; cout << "Your choice is " << iChoice << endl; } while (iChoice != 3); return 0; }
lecture 5_1
// Array and Pointer (1) #include <iostream> #include <string.h> #include <stdio.h> using namespace std; void intSwap1(int num1, int num2) { int temp{num1}; num1 = num2; num2 = temp; } void intSwap2(int* num1, int* num2) { int temp{*num1}; *num1 = *num2; *num2 = temp; } int main() { int ID = 201911999; cout << "address of studentID :" << &ID << endl; // & -> 변수의 주소 줌 cout << "value at " << & ID << " : " << *(& ID)<<endl; // * -> 주소의 값 줌 cout << endl; // Pointer /* Declaration data_type * variable_name; */ int iNum = 10; // int* pNum = &iNum; // 주소형 자료 pNum에 iNum 변수의 주소를 저장함 cout << "iNum : " << iNum << endl; //iNum : 10 cout << "pNum : " << pNum << endl; // pNum : 0x16fdff204 cout << "*pNum(=iNum) : " << *pNum << endl; // *pNum(=iNum) : 10 cout << "&iNum(=pNum) : " << &iNum << endl; // &iNum(=pNum) : 0x16fdff204 cout << endl; *pNum = 20; cout << "*pNum : " << *pNum << endl; // *pNum : 20 cout << "iNum : " << iNum << endl; // iNum : 20 cout << endl; // 주소 값은 8 byte 가진다??? cout << sizeof(char) << endl; // 1 cout << sizeof(char*) << endl; // 8 cout << sizeof(int) << endl; // 4 cout << sizeof(int*) << endl; // 8 cout << endl; int iNum1{ 10 }; int iNum2{ 30 }; cout << iNum1 << " " << iNum2 << endl; // 10 30 /* num1 = num2; num2 = temp; */ intSwap1(iNum1, iNum2); cout << iNum1 << " " << iNum2 << endl; // 10 30 <- swap X /* *num1 = *num2; *num2 = temp; */ intSwap2(&iNum1, &iNum2); cout << iNum1 << " " << iNum2 << endl; // 30 10 <- swap O // 변수끼리 Swap 할 때에는 주소값을 함수에 넘겨야함... cout << endl; // implicit initialization char name[10]; name[0] = 'J'; name[1] = 'o'; name[2] = 'h'; name[3] = 'n'; cout << name << endl; // Johnxdfox01 <- 지정 안해준부분 이상한거 저장됨... // explicit initialization char name1 [10] = "John"; cout << name1[0] << endl; // J cout << name1 << endl; // John <- 지정 안한부분은 저장 안됨!! cout << endl; // APIs for string operations char str[20] = "Hello"; char str2[] = "World"; cout << strlen(str) << endl; // 5 cout << sizeof(str) << endl; // 20 //strncat_s(str, str2, 4); cout << str << endl; // Hello if (strcmp(str, "HelloWorld") == 0) cout << "OK" << endl; else cout << "Fail" << endl; // Fail char str01[] = "10"; char str02[] = "20"; cout << atoi(str01) * atof(str02) << endl; // 200 int studentID[][3] = { {1 }, {4, 5, 6} }; cout << studentID[0][0] << " "; // 1 cout << studentID[0][1] << " "; // 0 cout << studentID[0][2] << " "; // 0 cout << studentID[1][0] << " "; // 4 cout << studentID[1][1] << " "; // 5 cout << studentID[1][2] << endl;// 6 cout << sizeof(studentID) << endl; // 24 cout << sizeof(studentID[0]) << endl; // 12 return 0; }
lecture 5_2
// Array and Pointer (1)-2 #include <iostream> #include <string.h> #include <stdio.h> using namespace std; int main(){ /* multidimension array pointer int iNum = 0; int iNums[3] = { 1, 2, 3 }; int* pNum = &iNum; int* pNums1 = &iNums[0]; int* pNums2 = iNums; // &iNums ? cout << pNums1 << endl << pNums2 << endl; // 0x16fdff258 ; 0x16fdff258 cout << *pNums1 << endl << *pNums2 << endl; // 1 ; 1 cout << iNums[1] << endl << pNums1[1] << endl << pNums2[1] << endl; // 2 ; 2 ; 2 */ cout << endl; /* pinter arithmetics*/ int iNum[3]{ 1, 2, 3 }; int* pNum = iNum; // &iNum[0]; cout << pNum << endl; // 0x16fdff258 cout << pNum + 1 << endl; // 0x16fdff25c cout << pNum + 2 << endl; // 0x16fdff260 cout << *pNum << endl; // 1 cout << *(pNum + 1) << endl;// 2 (= pNum[1]) cout << endl; // row: 2, col : 3 int iNums[2][3] = { 1, 2, 3, 4, 5, 6 }; /* 1 2 3 4 5 6 */ cout << iNums << endl; // 0x16fdff240 <- 어레이 시작 주소 cout << *iNums << endl; // 0x16fdff240 <- 어레이 1열의 시작 주소 cout << **iNums << endl; // 1 <- (0,0) 위치 원소 cout << **(iNums + 1) << endl; // 4 <- (1,0) cout << *((*iNums) + 1) << endl; // 2 <- (0,1) cout << *((*(iNums + 1)) + 1) << endl; // 5 <- (1,1) cout << *((*(iNums + 1)) + 1) + 1 << endl; // 6 <- ((1,1) 위치 원소) + 1 = 5 + 1 = 6 return 0; }
lecture 6_1
#include <stdio.h> #include <iostream> using namespace std; void fun(int arr[])// SAME AS void fun(int *arr) { unsigned int n = sizeof(arr) / sizeof(arr[0]); // sizeof(arr) will return size of 'int *' instead of 'int[]' cout << "size of arr :" << sizeof(arr) << endl; // <- 8 출럭 cout << "size of arr[0] :" << sizeof(arr[0]) << endl; // <- 4 출력 cout << "Array size inside fun() is :" << n << endl; } void fun2(int arr[], unsigned int n) // SAME AS void fun2(int *arr, int n) { int i; for (i = 0; i < n; i++) cout << arr[i] <<endl; } int main(){ int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; unsigned int n = sizeof(arr) / sizeof(arr[0]); cout << "Array size inside main() is :" << n << endl; fun(arr); fun2(arr, n); /* Dynamic Memory Allocation data_type * ptr; ptr = new data_type; // Memory allocation delete ptr; // Memory deallocation */ int* ptr; //ptr = (int*)malloc(sizeof(int)); ptr = new int; *ptr = 123; cout << "value of * prt : " << *ptr; delete ptr; int x = 10; int* ptr1 = &x; // acess stack delete ptr1; // access heap // difference of delete and delete[] int* ptr2; ptr = new int[10]; // ptr = new int[10] {0}; ptr2[0] = 0; *(ptr2 + 1) = 1; for (int i = 0; i < 10; i++) *(ptr2 + i) = i; for (int i = 0; i < 10; i++) cout << ptr2[i] << " "; delete ptr2; delete[] ptr2; return 0; }
lecture 6_2
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; int main(){ // Memort Leak int* ptr = new int{ 10 }; int* ptr2 = new int{ 20 }; *ptr = 30; ptr = ptr2; // memory pointed by the ptr becomes garbage cout << ptr << " " << ptr2 << endl; cout << *ptr << " " << *ptr2 << endl; // Dangling pinter int* ptr3 = new int{40}; // Heap allocation cout << "Do something before delete" << endl; delete ptr3; //Heap deallocation *ptr3 = 10; cout << "Do something AFTER delete" << endl; return 0; // reference type //char & name; // error int iNum = 10; int & rNum = iNum; // reference type int* pNum = &iNum; // pointer type cout << "value:" << iNum << " address:" << &iNum << endl; cout << "value:" << rNum << " address:" << &rNum << endl; cout << "value:" << *pNum << " address:" << pNum << endl; rNum = 5; cout << "value:" << iNum << " address:" << &iNum << endl; cout << "value:" << rNum << " address:" << &rNum << endl; cout << "value:" << *pNum << " address:" << pNum << endl; *pNum = 50; cout << "value:" << iNum << " address:" << &iNum << endl; cout << "value:" << rNum << " address:" << &rNum << endl; cout << "value:" << *pNum << " address:" << pNum << endl; int a = 10; int* p; int** pp; // 이런 double... reference에서는 불가함 p = &a; pp = &p; return 0; }
lecture 6_3
#include <stdio.h> #include <iostream> using namespace std; void intSwap1(int num1, int num2) { int temp{num1}; num1 = num2; num2 = temp; } void intSwap2(int* num1, int* num2) { int temp{*num1}; *num1 = *num2; *num2 = temp;} void intSwap3(int & num1, int & num2) { int temp{num1}; num1 = num2; num2 = temp; } int * f1(){ int iNums[3] {1,2,3}; return iNums; } int * f2(){ int* iNums = new int[3]; //Heap allocation iNums[0] = 1; iNums[1] = 2; iNums[2] = 3; return iNums; } void f3(int arr[], int size){ for (int i=0; i<size;i++){ arr[i] = arr[i] + 10; } } int main(){ int iNum1{ 1 }; int iNum2{ 3 }; cout << iNum1 << " " << iNum2 << endl; intSwap3(iNum1, iNum2); cout << iNum1 << " " << iNum2 << endl; int * pNums = f1(); // int * pNums = f2(); // int * pNums = f3(); cout << *pNums << endl; delete[] pNums; return 0; }
lecture 7_2
#include <iostream> #include <cstring> using namespace std; /* 9/27 class - (1) Constructer / Destructor */ class Student { private: int* m_pID; string m_name; public: Student(); // Constructor (without parameters) Student(int, string); // Constructer (with parameters) ~Student(); // Destructor (without parameters) int GetID() { return *m_pID; } string GetName() { return m_name; } }; // Implementation // Constructor implementation (ver1) Student::Student(){ m_pID = new int(0); m_name = "Alice"; cout << "Constructor w/o parameter: " << m_name << endl; } // Constructor implementation (ver2) Student::Student(int id, string name){ m_pID = new int(id); m_name = name; cout << "Constructor with parameter: " << m_name << endl; } // Destructor implementation Student::~Student(){ cout << "Destructor: " << m_name << endl; delete m_pID; // Heap deallocation }; int main(){ // constructor 1 /* cout << "Place 1" << endl; Student s1(123, "Tom"); cout<< "Place 2" << endl; cout << s1.GetID() << " " << s1.GetName() << endl; cout << "Place 3" << endl; */ // constructor 2 /* cout << "Place 1" << endl; Student* s1; // pointer at stack cout<< "Place 2" << endl; Student* s2; // pointer at stack cout << "Place 3" << endl; s1 = new Student(123, "John"); // Heap allocation (Ver2) cout << "Place 4" << endl; s2 = new Student(); // heap allocation (Ver1) cout << "Place 5" << endl; Student s3; // stack allocation cout << "Place 6" << endl; Student s4(456, "Jenny"); cout << "Place 7" << endl; Student many_students[3]; // stack allocation cout << "Place 8" << endl; */ // destructor 1 /* cout << "Place 1" << endl; Student* s1; // pointer at stack cout<< "Place 2" << endl; s1 = new Student(123, "John"); // Heap allocation (Ver2) cout << "Place 3" << endl; delete s1; // heap deallocation cout << "Place 4" << endl; */ // destructor 2 cout << "Place 1" << endl; Student s1; // Stack allocation Student s2(2, "Jenny"); cout << "Place 2" << endl; Student* s3; cout << "Place 3" << endl; s3 = new Student(4, "Tom"); cout << "Place 4" << endl; delete s3; cout << "Place 5" << endl; return 0; }
lecture 7_3
#include <iostream> #include <cstring> using namespace std; /* class - (2) Destructer */ class Student { private: int* m_pId; string m_name; public: Student(); //Constructor 1 Student(int, string); //Constructor 2 ~Student(); //Destructor int GetID() { return *m_pId; } void setID(int); }; void Student::setID(int ID) { *m_pId = ID; //dereferencing으로 m_pId를 ID로 업데이트 *(this->m_pId) = ID; } //Constructor 1 Implementation Student::Student() : Student(0, "") { cout << "Constructor 1" << m_name << endl; //version 1이 불릴 때 우리가 다시 version 2에 대한 정보를 불러서 initialization하는 과정을 거쳤기에 //해당되는 constructor 2를 먼저 수행하기에 constructor 2가 찍힌다 } //Constructor 2 Implementation Student::Student(int id, string name) { m_pId = new int{ id }; m_name = name; cout << "Constructor 2" << m_name << endl; } //Destructor Implementation Student::~Student() { if (m_pId != nullptr) { delete m_pId; m_pId = nullptr; } cout << "Destructor" << m_name << endl; } int main() { Student s1; //Stack allocation Student* s2; //Pointer s2 = new Student(123, "Jenny"); //Heap allocation //Heap에 allocation할 때 constructor가 실제로 불리게 됨 //s2가 s1보다 먼저 deallocation 됨 //따라서 Destructor라는 메시지 다음에 Jenny가 찍히게 됨 cout << s2->GetID() << endl; s2->setID(456); cout << s2->GetID() << endl; delete s2; //Heap deallocation } /* class Student { private: int* m_pID; string m_name; public: Student(); // Constructor 1 (without parameters) Student(int, string); // Constructer 2 (with parameters) ~Student(); // Destructor (without parameters) int GetID() { return *m_pID; } void SetID(int); }; void Student::SetID(int ID){ //*m_pID = ID; *(this->m_pID) = ID; } // Implementation // Constructor 1 implementation Student::Student() : Student(0,"") { cout << "Constructor 1 " << m_name << endl; } // Constructor 2 implementation Student::Student(int id, string name) { m_pID = new int(id); m_name = name; cout << "Constructor 2 " << m_name << endl; } // Destructor implementation Student::~Student() { if (m_pID != nullptr) { delete m_pID; m_pID = nullptr; } cout << "Destructor " << m_name << endl; delete m_pID; // Heap deallocation }; int main(){ Student s1; // Stack allocation Student* s2; // pointer s2 = new Student(123,"Jenny"); // Heap Allocation delete s2; return 0; }*/
Trash can
#include <iostream> using namespace std; class Student { private: int* m_pID; string m_name; public: Student(); // constructor without param Student(int, string); // constructor with param ~Student(); // destructor는 param 안받는 버전만 가능함 int GetID() { return *m_pID; } string GetName() { return m_name; } // private 영역에 간접적으로 접근 }; Student::Student() { m_pID = new int(0); m_name = "Alice"; cout << "constructor without param : " << m_name << endl; } Student::Student(int id, string name) { m_pID = new int(id); m_name = name; cout << "constructor with param : " << m_name << endl; } Student::~Student() { cout << "destructor : " << m_name << endl; delete m_pID; } int main() { /* // stack cout << "place 1" << endl; Student s1; // stack allocation -> 이 때는 delete 하지 않아도 destructor 됨 cout << "place 2" << endl; */ /* // heap cout << "place 1" << endl; Student* s1; cout << "place 2" << endl; s1 = new Student(123, "Joun"); // heap allocation cout << "place 3" << endl; delete s1; // 이거 해야지 destructor가 정상적으로 수행됨 heap deallocation cout << "place 4" << endl; */ // heap and stack cout << "place 1" << endl; Student s1; // stack allocation -> 이 때는 delete 하지 않아도 destructor 됨 Student s2(2, "Jenny"); cout << "place 2" << endl; Student* s3; // pointer일 뿐 실제 객체가 생성되는 건 아니다 cout << "place 3" << endl; s3 = new Student(4, "Tom"); cout << "place 4" << endl; delete s3; cout << "place 5" << endl; // stack에서 사라질 때는 s2가 먼저 지워지고 s1이 나중에 지워짐 : stack은 LiFo이기 때문이다. return 0; }
#include <iostream> using namespace std; class Student { private: int* m_pID; string m_name; public: Student(); // constructor without param Student(int, string); // constructor with param int GetID() { return *m_pID; } // private 영역에 간접적으로 접근 string GetName() { return m_name; } // private 영역에 간접적으로 접근 }; Student::Student() { // constructor without param 선언 m_pID = new int(0); m_name = "Alice"; cout << "constructor without param : " << m_name; } Student::Student(int id, string name) { // constructor with param 선언 m_pID = new int(id); m_name = name; cout << "constructor with param : " << m_name; } int main() { Student s1; // constructor with param 출력 cout << "place 1" << endl; Student s1(123, "Tom"); // constructor with param 출력 cout << "place 2" << endl; cout << s1.GetID() << " " << s1.GetName() << endl; cout << "place 3" << endl; cout << "place 1" << endl; Student* s1; // 포인터를 선언한 것이므로 안된다(?) 포인터 in stack cout << "place 2" << endl; Student* s2; // 포인터를 선언한 것이므로 안된다(?) 포인터 in stack cout << "place 3" << endl; s1 = new Student(123, "John"); // heap allocation cout << "place 4" << endl; s2 = new Student(); // heap allocation 파라미터 없는 버전 cout << "place 5" << endl; Student s3; // stack allocation cout << "place 6" << endl; Student s4(123,"Jenny"); // stack allocation cout << "place 7" << endl; Student many_students[3]; // stack allocation cout << "place 8" << endl; return 0; }
