Abstraction(추상화) : 공통의 속성이나 기능을 묶어 이름을 붙인다. (”일반화 한다”라고 생각해도 된다)
Encapsulation(캡슐화) : Object의 implemention을 구체화 시킨다. (ex. member function, member variable)
Polymorphism(다형성) : 하나의 interface가 다양한 type을 가질 수 있다. (overloading, overriding)
Inheritance(상속) : class가 서로 부모 자식 관계를 가진다. (is a relationship)
is a relationship vs has a relationship
has-a : association 관계일 때 적합하다.
is-a : inheritance 관계일 때 적합하다.
Inheritance
상속이 필요한 이유는 다음과 같다.
reuse code (class)
maintain hierarchical class structure
need more specified objects
밑과 같은 상황에서 사용한다.
is-a relationship일 때
has-a relationship일 때도 가능은 하다.
위와 같이 공통된 부분은 상속을 통해서 구현하고 이후에는 각각의 class마다 필요한 것들을 specialized 시킨다. (중복 구현을 안하게 해준다!)
문법은 다음과 같다. ‘자식 클래스의 이름’ : ‘ 상속 타입 ‘ ‘부모 클래스’
inheritance type(상속 타입)에 따라 child class의 member access 범위가 달라진다.
참고 : protected는 자식 class 내에서 사용 가능하고, 자식 class 내에서 private 취급을 받는다.
private는 외부 뿐만 아니라 자식 class에서도 사용 불가능하다.
parent의 private 변수/함수는 child에서 접근 불가능하다.
protected인 변수/함수는 상속 타입에 따라 child에서 protected 또는 private가 될 수 있다.
public인 변수/함수는 상속 타입을 그대로 따라간다.
EX 1.
Student class를 살펴보자. 이 class에서는 GetStudentName()함수를 정의하고 있다. 이 함수는 m_name 변수를 불러오고 있다. Student class의 상속 타입은 public이다. 따라서 protected(public에서는 protected to protected) 영역에 있는 m_name 변수에 접근 가능하다. 따라서 위 implementation에서는 error가 발생하지 않는다.
Teacher class를 살펴보자. 이 class에서는 GetTeacherID()함수를 정의하고 있다. 이 함수는 m_id 변수를 불러오고 있다. m_id 변수는 private영역에 정의되어 있다. 따라서 상속 타입이 public이어도 자식 class에서는 접근이 불가하다. 위 implementation은 error를 발생시킬 것이다. m_id 변수를 protected로 바꾸거나 m_id 변수 대신 GetID() 함수를 사용하면 정상 동작 시킬 수 있다.
EX 2.
main 함수를 살펴보자. t1이라는 Teacher class 인스턴스를 통해 SetName 함수를 호출하고 있다. 이 때 Teacher class는 상속 타입이 protected이다. protected일 때는 부모 class의 public 영역 또한 protected가 되므로 SetName 함수는 Teacher class 내에서 private 취급을 받게 된다. 따라서 위 main은 error를 발생시킨다. 상속 타입을 public으로 바꾸거나 Teacher class의 public 영역에 SetTName함수를 추가시키는(이 때는 main에서도 SetTName 함수를 불러와야 할 것이다) 등의 방법으로 문제를 해결할 수 있다.
Type Conversion (1) - up casting
기본 자료형에서도 int↔float 등의 type conversion이 가능했다. class에서도 이가 가능하다.
우리는 여기서 type을 바꿨을 때 발생하는 문제에 대해 잘 알아야 한다.
up casting : 자식 클래스(derived)의 reference 혹은 pointer를 부모 클래스(base class)로 바꾸는 것이다.
Base * pB = &d;이러한 방식으로 up casting을 할 수 있다.
Person* p_arr[] = { &s1, &t1 }; 위 코드에서는 이 부분을 통해 upcasting을 진행 하였다.
overloading : 다른 타입의 파라미터를 사용하고 싶을 때 사용한다. / Inheritance가 없을 때도 사용할 수 있다. / 같은 함수에 대해 signiture를 바꾸며 다양한 정의를 할 수 있다.
overriding : 자식 클래스에서 함수의 기능을 바꾸고 싶을 때 사용한다. / 상속 되었을 때만 사용할 수 있다. / 같은 signiture를 가지는 것들을 각각 다른 기능을 하도록 바꾼다.
밑은 각각 overloading과 overriding의 예시이다.
overloading example
overriding example
overloading은 polymorphism 부분을 참고하도록 하자.
overriding의 예시이다. 같은 파라미터를 가진 같은 이름의 함수를 서로 다른 기능을 가지도록 정의한 것을 확인할 수 있다.
이때 up casting을 진행하게 되면 어떻게 될까?
다음은 up casting 코드 예시이다.
(주의 : constructor에는 base class의 것이 먼저 출력되지만, overriding에서는 child의 것이 먼저 출력된다)
우리는 Person work 위 결과에서의 부분의 출력을 원하지 않는다. 이 때는 virtual keyword를 사용하면 된다.
Virtual Function
base class 내에서 선언되고 derived class에서 override된 멤버 함수는 base class 내에서 virtual function이라 정의할 수 있다.
이후 derived class의 객체가 base class를 통한 포인터나 참조를 통해 사용될 때, 재정의(override)된 가상 함수를 호출하면 derived class에서의 함수 동작이 실행된다. (대충 up casting해도 파생 class의 함수를 호출할 수 있다는 이야기이다)
virtual function은 dynamic binding 형태이며 run-time 내에 실행된다.
(질문 : 그럼 derived class를 그냥 사용하면 되지 왜 굳이 up casting을 거칠까? 인터넷에 따르면 다형성을 통해 코드의 재사용성을 높이기 위해서라고 한다. 이에 대해서는 나중에 다시 알아보자)
virtual의 개념은 deconstructor에서도 적용된다, 예시는 다음과 같다. up casting 이후에 본래의 deconstructor이 불려지지 않는 문제를 해결할 수 있다.
Pure Virtual Function
base class에서 function을 implementation할 수 없는 경우가 있다. 그럴 때는 pure virtual을 사용하면 된다.
(주의 : 이 때 ‘하나 이상’의 pure virtual를 가지는 class를 abstract class라고 한다. 추상적으로 밖에 디자인 되지 않은 class라는 것을 의미한다. 이 class 스스로는 instance, 즉 객체가 될 수 없다. function이 define되지 않았으니 당연하다)
다시 설명하자면 pure function은 디테일한 부분이 child class에서 정의되어야 할 때 사용된다. (자식 클래스에는 이러한 기능이 ‘꼭’ 정의되어 있어야 한다)
다음은 예시이다.
위 코드는 abstract class인 person의 객체를 생성하려하고 있다. 또한 pure function인 work를 teacher derived class에서 재정의 해주지 않고 있다. 따라서 위 코드에서는 error가 출력된다
S,L은 P의 자식, D는 S의 자식이라고 생각해보자.
P
^
S L
|
D
이 때 D를 불러올 때 S에만 work가 있으면 D에 work가 없어도 잘 동작한다. 만약 D에만 work가 있으면 어떨까? 이 때도 잘 동작한다. (메모는 이렇게 되어 있으나 확실하지 않으니 나중에 한 번 확인해보자)
Etc…
interface class : virtual function만을 가지는 class이다.
final function : 마지막 function이다. 자식 class에서는 더 이상 이 함수를 가질 수 없음을 의미한다.
Syntax error는 문법 오류이다. Run-time error는 실행 이후에 ‘어떠한 이유’로 생기는 에러이다 (내가 C++를 다룰 때 가장 발생하는 오류인 것 같다. 이를 잘 다루는게 중요하다. 이 오류는 에러를 출력시키지 않을 때도 있다). Semantic error 또는 Logical error는 의도에 맞지 않게 결과가 출력되는 오류를 의미한다.
예시 사진은 다음과 같다.
Try-Throw-Catch
위 error말고도 handling 해주어야 할 것들이 있다. (텀 프로젝트 과제로 주어졌던 ATM에서 handling 했던 것 처럼)
만약 위 사항들을 if else문으로 처리한다면 코드가 매우 복잡해지고, 길어진다는 단점이 있다.
Try-Throw-Catch
Try-Throw-Catch 문법을 이용하면 이러한 것들을 좀 더 손쉽게 handling할 수 있다.
문법은 다음과 같다. try 안에서 error 사항을 잡고, catch 부분에서 error 사항을 처리하면 된다.
multi-Try-Throw-Catch
다중 Try-Throw-Catch문은 다음과 같이 정의하면 된다. throw한 것과 일치하는 파라미터 타입을 가지고 있는 catch문에 들어가서 error 처리를 진행한다.
catch(…)는 위 throw가 위의 어떤 catch문에도 걸리지 않았을 때 실행된다. default 값으로 사용한다고 생각해도 된다. 만약 이것도 없다면 computer 내의 default를 수행한다. ( “terminate called after throwing an instance of ‘int’”라는 에러를 출력하며 종료할 것이다)
nested trying and catching
밑처럼 복잡하게 얽혀있을 때 어느 부분에서 try-catch가 일어나는지 잘 살펴보아야 한다.
Example
Try-Throw-Catch문의 예시이다.
Example1 - Throw without Catch
이 예시에서는 throw 부분은 있지만 catch 부분이 없는 것을 확인할 수 있다. 위 상황에서 예외 사항 (ex 1111)이 입력된다면 “terminate called after throwing an instance of ‘int’”라는 에러가 출력되며 프로그램이 종료된다.
Example2 - Try-Throw-Catch
위 코드에서는 catch 부분이 잘 정의되어 있다. 이 경우에는 Age( 1111 ) is not valid가 잘 출력된다. (만약 try 없으면 어떻게 될까? 오류가 생긴다. try-chach는 단짝이다)
Example3 - multiple catches
위 코드는 다중 에러 처리를 진행한다. name은 string 변수이니 catch (string s)에서 에러 처리가 될 것이다. age는 int 변수이니 catch (int i)에서 에러 처리가 될 것이다.
Etc…
stack unwinding
함수 내에 catch가 없으면 main 함수로 돌아가서 catch문을 수행한다.
(이 현상을 ‘함수를 호출한 영역으로 예외 데이터가 전달되는 현상’ stacking unwiding, 스택 풀기 라고 한다)
위 사진을 살펴보자. 위는 overloading과 overriding을 함께 사용했을 때 나타나는 예시이다.
overloading만을 사용했다면 hello()가 잘 출력되었을 것이다. 하지만 위해서는 overriding을 하기 위해서 virtual keyword를 사용하였다. 때문에 자식 클래스에서 파라미터가 없는 hello 함수를 출력했을 때 오류가 생긴다. 이는 Option1 또는 Option2를 통해서 해결할 수 있다.
Template는 이를 해결해 줄 수 있다. Templete를 이용하면 같은 기능을 하는 함수를 굳이 여러 파라미터에 대해 정의해주지 않아도 되고, 따라서 위와 같은 실수도 예방할 수 있게 된다.
문법은 다음과 같다. 맨 위에 template<typename T>를 적고, 사용할 때 Typename<Type> 으로 타입을 정해서 사용하면 된다.
다음은 함수에 대한 Template사용 예시이다.
class에서도 template를 활용할 수 있다. 예시는 다음과 같다.
변수 두 개를 이용할 수도 있다. 예시는 다음과 같다.
변수 두 개를 사용하되 하나를 default 값으로 설정할 수도 있다. 예시는 다음과 같다.
(주의 : default는 무조건 두 번째 변수만 가능하다. 만약 T1을 default로 설정하고 T2를 default로 설정하지 않으면 오류가 발생하게 된다)
type 대신 value를 넣어줄 수도 있다. 예시는 다음과 같다.
template를 specialization할 수도 있다. 예시는 다음과 같다.
char + char는 불가능하기 때문에 const char* Add 함수를 따로 만들어준 것이다.
그럼 다른 기능을 제공할거면 template 대신 overloading을 쓰는게 더 편하지 않을까?
챗지피티의 말로는 template은 컴파일 타임에 동작하기 때문에 더 효율적인 동작이 가능하다고 한다.
함수를 사용하는 이유는 Readability (가독성), maintainability (유지보수성), code reuse (재사용성) 세 가지 때문이다.
- 함수 정의
함수에서는 총 4가지가 정의 되어야 한다.
return type, function name, parameters, function body
밑의 코드를 보면 (double) return type, (calArea) function name, (int radius) parameters, ({}) function body가 정의 되어 있는 걸 확인할 수 있다.
또한 함수가 정의될 때는 선정의 되어야 한다.
선정의를 안하고 main함수부터 정의하게 된다면 오류가 생긴다.
밑의 코드와 같은 형태가 옳은 표현이다.
만약 main 함수를 먼저 쓰고 싶다면 declaration을 통해서 미리 정의해도 된다.
declaration은 return type, function name, parameters를 이용해서 할 수 있다.
마지막으로 모든 함수에는 return 값이 존재해야 한다.
만약 return 값이 없는 함수를 이용하고 싶다면 void를 이용해야 한다.
Overhead of function calls
함수가 호출되면 동작이 끝날 때 까지 일시적으로 stack에 할당 된다.
이 때 함수가 너무 많이 호출되면 stack과 heap이 만나면서 stack overflow가 되면서 exceptional한 결과가 출력된다.
밑 코드는 위와 같은 오류에 대해서 다루고 있다.
여기서는 두 가지 오류가 발생할 수 있다.
첫 번째 오류는 type 오류이다.
위 코드에 13을 입력하면 오류가 생긴다. 이유는 long은 -2147483648~2147483647 까지의 값을 지원하는데 13!은 그 이상의 값을 가지기 때문이다. 이 때는 undefined 된 동작을 수행하게 된다.
두 번째 오류는 recursion 오류이다.
10000을 입력하면 오류가 생긴다. 이유는 10000을 넘기면 함수가 10000번 호출 되기 때문에 stack와 heap이 만나게 되기 때문이다. 이 때는 exceptional한 결과가 출력되게 된다.
Macro and Inline function
- Macro function
이는 fuction의 역할을 하지만 stack의 공간을 차지하지는 않는 function이다.
preprocessor가 compile전에 이를 모두 바꿔준다.
문법은 다음과 같다.
다음과 같이 사용할 수 있다.
- Inline function
Mecro funciton과 마찬가지로 stack의 공간을 차지하지 않는 function이다.
문법과 사용법은 밑의 코드를 확인하면 된다.
이 함수들을 사용하는 이유는 함수들을 code text로 치환 시켜서 stack에 function이 allocated 되는 걸 막기 위해서 이다.
마지막으로 세 형태의 함수들의 특징을 정리하면 다음과 같다.
function call은 stack에 메모리를 저장하므로 매크로나 인라인 보다 메모리를 더 많이 소모한다. 매크로는 preprocessor에서 처리되면 메모리를 효율적으로 사용할 수 있다. 인라인은 컴파일러에 의해서 처리되지만 compiler가 복사본을 저장해두고 매번 불러오지 않기 때문에 메모리를 아낄 수 있다.
하지만 program의 size를 늘릴 수도 있고, side effect가 있을 수 있기 때문에 그런 걸 고려하면서 사용하면 좋다.
Local VS Global variables, Static variables
우리가 정의한 변수를 프로그램 어디서나 항상 사용할 수 있는 것은 아니다.
우리는 변수의 lifetime과 scope를 알아야 한다.
local variable의 lifetime과 scope는 {} 내에서만 적용된다.
global variable의 lifetime과 scope는 프로그램이 끝날 때 까지 적용된다.
static variable lifetime은 프로그램이 끝날 때까지 적용되고, scope는 {} 내에서만 적용된다.
밑의 코드 예제를 확인 해보자.
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 되게 된다.
(위 예제에서 전위 후위 연산자가 사용되었다. 생각보다 헷갈리니 잘 봐두도록 하자)
다음 예제를 주의깊게 보자. 전년도 기출이다.
이 문제에서 temp가 계속 바뀐다. 이걸 무시하고 풀면 (문제를 제대로 안읽고 풀면) 답이 틀리니 주의하도록 하자.
Etc..
- Header file
우리는 header file을 사용하므로써 module화를 해 management할 수 있다.
파이썬에서 import와 같다.
위의 그림과 같이 사용할 수 있다.
(여기서 int Multiply (int, int =1)이 뭘 의미하는지 헷갈렸음. 이 뒤에 int=1은 default 값 정의해 준거라고 생각하면 됨!)
밑의 코드에서는 double type을 이용하기 때문에 ‘+1 → 8bytes 뒤로 이동’이 될 것이다.
밑에도 복습을 위한 예시 코드이다.
Passing Array to Function
배열 parameter로 전달할 때 전달 받는 함수는 배열의 크기를 알 수 없다.
우리는 배열의 주소만을 인자로 전달해주기 때문이다.
이 때 발생하는 문제가 있다. 밑의 코드를 통해 살펴보자.
(주의 사항 : 밑 코드에서 함수의 parameter 형식으로 int arr[]와 int *arr를 사용했다. 그럼 배열을 정의/초기화 할 때도 int arr[]= {1,2,3,4} 말고, int arr* = {1,2,3,4}로 정의할 수 있지 않을까 라고 생각할 수 있다. 하지만 이는 불가능하다. 정의를 저렇게 한다면 포인터인지 배열인지 구분할 수 없게 되기 때문이다)
위 코드에서 세 번째 출력 결과가 이상함을 확인할 수 있다.
이는 우리가 함수의 size를 함께 전달하지 않아서 그렇다.
arr가 인자로 넘어가는 과정을 생각해 보아야 한다. 우리는 parameter로 arr의 시작 주소를 전달해 주었다. 함수는 이 시작 주소를 바탕으로 다음 주소를 알아내어 동작을 할 것이다. 즉 우리는 배열을 전달하지 않고 첫 주소 만을 전달한 것이다. 따라서 위 코드의 fun 함수는 배열의 size를 알 수 없고, 그렇기 때문에 exceptional한 결과를 출력시킨다.
따라서 우리는 배열을 parameter로 전달할 때 배열의 size도 함께 전달해 주어야 한다.
밑 코드는 수정한 결과이다.
밑의 코드에서도 size를 주지 않으면 이상한 결과가 출력되었을 것이다. 하지만 size를 인자로 함께 넘겨주었기 때문에 expectation한 결과가 나온다.
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에서 이루어진다. 따라서 주의해야 할 점이 있다.
위의 코드에서는 run-time오류가 생기는데 이유는 stack과 heap이 함께 쓰였기 때문이다. 할당은 stack에서 했는데 deallocate는 heap에서 하려고 하니까 오류가 생기는 것이다.
- Syntax
문법은 다음과 같다.
예제 코드는 다음과 같다.
일차원 배열에서의 쓰임은 밑의 코드와 같다.
배열에서의 동적할당은 매우 중요하다. 동적할당을 이용하면 배열의 크기를 파라미터로 지정할 수 있다.
밑의 예제 코드를 살펴보자.
기존 배열에서는 배열의 크기를 파라미터로 지정하지 못한다 (배열의 크기를 지정할 때 문자를 넣으면 오류가 생기는 것을 확인할 수 있다). 동적할당은 이를 가능하게 한다. 중요한 특징이니 알아두자.
(추가 : 배열의 크기를 문자로 정하고 싶으면 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만나 오류가 생길 수도 있을 것이다.
배열에서의 deallocation은 다음과 같이 진행한다.
for문으로 배열의 모든 원소를 지우고 난 후에 배열을 지워야 한다.
Reference Type
reference type은 ‘별명’이라고 생각하면 좋다. 즉 이미 있는 값에 대해서 별명을 붙여주는 것이다.
- Syntax
문법은 밑의 그림과 같다.
위를 바탕으로 작성한 코드이다.
위 코드에서 intializaion을 할 때 첫 줄에서 오류가 생기는 것을 확인할 수 있다. reference type은 이미 있는 값에 대한 별명이기 때문에 initialization을 하지 않은 새로운 변수에 refercence type을 설정하려고 하면 오류가 생기는 것이다. (교수님은 이에 대해서 refererence type에는 null이라는 개념이 없는 것이라고 설명하셨다)
즉 initialization 한 변수에 한해서 reference type이 지정 가능하다.
int& rNum = iNum; 이 줄은 “iNum 변수에 대하여 rNum이라는 별명 지정해 준다” 정도로 이해해 주면 된다.
밑의 코드는 위의 코드에 좀 더 추가해서 적은 것이다. 읽어보고 이해해보자.
- Difference of reference and pointer
Pointer와 Reference는 주소에 접근한다는 점에서 비슷하다고 할 수 있다. 하지만 몇 가지 차이가 존재한다.
변수 주소와 변수 주소의 주소
포인터는 변수 주소와 변수 주소의 주소가 다르다. 하지만 레퍼런스는 동일하다.
선언과 초기화
포인터는 선언 이후에 초기화를 해주어도 되지만 레퍼런스는 무조건 선언과 동시에 초기화를 해주어야 한다.
참조 변수에 접근하는 방법
포인터는 * operator를 사용하여 dereference해서 참조 변수에 접근한다. 레퍼런스는 단순히 변수 이름만으로 참조 변수에 접근한다. (즉 포인터는 *a 형태로 변수에 접근하지만 레퍼런스는 a만으로도 변수에 접근할 수 있다)
- Call by value, Call by reference
swap 함수를 reference를 이용해서 다시 구현해보자. 결과는 밑 코드와 같다.
reference를 이용한 swap 함수는 잘 동작한다.
swap1은 call by value를 이용한 동작이고, swap2와 swap3은 call by reference를 이용한 동작이다.
- etc.. (reference와 관련 없지만 교수님이 함께 설명하셔서 여기 넣어둠)
밑의 코드를 살펴보자.
출력 시켜보면 1이라는 값이 나옴을 확인할 수 있다.
그럼 만약 *(pNums+1)을 출력시켜보면 어떤 결과가 나올까?
trash 값이 나오는 걸 확인할 수 있다.
이는 함수 자기 자신이 종료되면 함수 내 값들이 모두 deallocate 되기 때문에 발생하는 문제이다.
그럼 이는 어떻게 해결할 수 있을까?
heap allocation을 이용하면 된다.
heap을 이용해서 공간을 할당하고 다 사용한 후 delete 해주면 된다.
기존의 예제에서는 main 함수가 array를 파라미터로 넘기는 것을 확인해 보았다.
이번에는 함수에서 return으로 array를 넘기는 과정을 보았다.
main 함수는 프로그램이 종료될 때 까지 살아있고 그 외의 함수는 자기 자신이 종료되면 죽기 때문에 파라미터가 array인 것과 return 값이 array인 것에는 차이가 있을 것이다. 주의하도록 하자.
++ c++에서는 life time 때문에 값이 비정상적으로 출력 되는 문제가 많다. static 변수, pointer, reference, dynamic allocation 등을 이용해서 이러한 문제를 해결할 수 있어야 한다.
Functional Pointer
Functional Pointer는 직접 부르지 않고 함수의 주소를 호출한다.
Pointer and Const
restriction이 더 작은 걸로 옮기면 error가 생긴다.
const는 전에도 언급했지만 변경할 수 없는 값들이다. 이 const 변수를 변경 가능한 일반 변수에 할당한다면 에러가 생길 것이다. restriction이 더 작은 걸로 옮겨갔기 때문이다.
이걸 바탕으로 밑 코드를 살펴보자.
첫 번째 코드는 잘 돌아간다.
두 번째 코드에서는 에러가 생긴다. const int*를 int로 바꾸려고 했기 때문이다.
세 번째 코드에서는 에러가 생긴다. cont int*를 const int*로 잘 바꾸었지만 dereferencing을 통해 const 변수의 값을 바꾸려고 했기 때문이다.
네 번째 코드는 잘 돌아간다. *int를 const int*로 바꾸었지만 이는 restriction이 커지는 방향으로의 이동이기 때문에 결과에 영향을 미치지 않는다.
class Complex {
int* m_r; // real part
int* m_i; // imaginary part
public:
……
friend ostream& operator<< (ostream& o, Complex c);
};
ostream& operator<< (ostream& o, Complex c)
{
o << *c.m_r << (*c.m_i < 0 ? "" : "+") << *c.m_i << "j" ; // 11 line
return o;
}
int main(){
cout << Complex(23, 7) << endl;
cout << Complex(-5, 20) << endl;
return 0;
}
class Vector {
int* m_data; // pointer to dynamic array
int m_size; // array size
public:
Vector() : m_data(new int[100]{ 0, }), m_size(100) { }; // data를 100개 생성하고
// 0으로 초기화 시킨다.
// size는 100으로 update한다.
~Vector() { delete [] m_data; }
int& operator[] (int);
};
int& Vector::operator[] (int index) {
if (index >= m_size)
{
cout << "Error: index out of bound"; // index 100을 넘어가면 에러를 출력해준다.
exit(0);
}
return m_data[index];
}
int main(){
Vector v;
cout << v[1]; // 0
v[1] = 20;
cout << v[1]; // 20
}
class Complex {
int* m_r; // real part
int* m_i; // imaginary part
public:
……
Complex & operator=(const Complex & c);
};
Complex & Complex ::operator=(const Complex & c) {
if (this == &c)
return *this;
delete m_r;
delete m_i;
m_r = new int(*c.m_r);
m_i = new int(*c.m_i);
return *this;
}
int main() {
int a = 1; int b = 1111;
int c = 2; int d = 2222;
int e = 3; int f = 3333;
Complex c10(a, b); Complex c11(c, d); Complex c12(e, f);
(c12 = c11) = c10; //Expect 1.1111
c10.print(); c11.print(); c12.print();
return 0; }
class Person {
private:
int* m_Id;
protected:
string m_name;
public:
Person() { m_Id = new int(1111); m_name = "None"; };
string GetName() { return m_name; };
int GetID() { return *(this->m_Id); }
void SetName(string name) { m_name = name; }
void SetID(int id) { *(this->m_Id) = id; }
};
class Student : public Person{
private: //Nothing
public:
string GetStudentName() { return this->m_name; } //OK
};
class Teacher : public Person {
private: //Nothing
public:
int GetTeacherID() { return *(this->m_Id); } //Error
};
int main() {
Student s1;
Teacher t1;
s1.SetName("Tom");
t1.SetName("Jenny");
cout << s1.GetStudentName() << endl;
cout << t1.GetTeacherID() << endl;
return 0;
}
class Person {
public:
…
void work() { cout << "Person Work" << endl; }
string GetClassName() { return "Person"; }
};
class Student : public Person {
public:
…
void work() { cout << "Student studies" << endl; }
string GetClassName() { return "Student"; }
};
class Teacher : public Person {
public:
…
void work(){ cout << "Teacher teaches" << endl; }
string GetClassName() { return "Teacher"; }
};
int main() {
Student s1;
Teacher t1;
s1.work();
cout << s1.GetClassName() << endl;
t1.work();
cout << t1.GetClassName() << endl;
Person* p_arr[] = { &s1, &t1 }; // up casting
p_arr[0]->work();
cout << p_arr[0]->GetClassName() << endl;
p_arr[1]->work();
cout << p_arr[1]->GetClassName() << endl;
return 0;
}
----
Student studies
Student
Teacher teaches
Teacher
Person work
Person
Person work
Person
class Person {
public:
virtual void work() { cout << "Person Work" << endl; }
virtual string GetClassName() { return "Person"; }
//Covered in later lecture
//virtual void work() = 0;
//virtual string GetClassName() = 0; //This is also ok
};
class Student : public Person {
public:
void work() { cout << "Student studies" << endl; }
string GetClassName() { return "Student"; }
};
class Teacher : public Person {
public:
void work(){ cout << "Teacher teaches" << endl; }
string GetClassName() { return "Teacher"; }
};
int main() {
Student s1;
Teacher t1;
s1.work();
cout << s1.GetClassName() << endl;
t1.work();
cout << t1.GetClassName() << endl;
Person* p_arr[] = { &s1, &t1 };
for (int i = 0; i < 2; i++) {
p_arr[i]->work();
cout << p_arr[i]->GetClassName() << endl;
}
return 0;
}
----
Student studies // up casting 안 한 결과
Student
Teacher teaches
Teacher
Student studies // up casting 한 이후의 결과
Student
Teacher teaches
Teacher
class Person {
private:
int* m_Id;
string m_name;
public:
Person() { m_Id = new int(1111); m_name = "None"; cout << "Person constructor 1 called" << endl; };
Person(int id) { m_Id = new int(id); m_name = "None"; cout << "Person constructor 2 called" << endl; }
virtual ~Person() { cout << "Person Destructor called" << endl; }
};
class Student : public Person {
public:
Student() : Person(10) { cout << "Student constructor called" << endl; };
~Student() { cout << "Student Destructor called" << endl; }
};
class Teacher : public Person {
public:
Teacher() { cout << "Teacher constructor called" << endl; }
~Teacher() { cout << "Teacher Destructor called" << endl; }
};
int main() {
Teacher* t1 = new Teacher();
delete t1;
Person* p_person = new Student(); //Upcasting
delete p_person;
return 0;
}
----
Person constructor 1 called
Teacher constructor called
Teacher Destructor called
Person Destructor called
Person constructor 2 called
Student constructor called
Student Destructor called
Person Destructor called
class Person {
public:
virtual void work( ) = 0;
};
class Student : public Person{
public:
//no implementation
};
class Teacher : public Person{
public:
void work( ) { }
};
int main() {
Person p1; // error due to the pure function
Student s1; // error due to no implementation of the pure function
Teacher t1;
return 0;
}
#include <iostream>
using namespace std;
int main() {
int age;
cout << "Your age ? ";
cin >> age;
if (age < 0 || age >= 150)
throw age;
cout << "Age: " << age;
}
#include <iostream>
using namespace std;
int main() {
int age;
cout << "Your age ? ";
try{
cin >> age;
if (age < 0 || age >= 150)
throw age;
cout << "Age: " << age;
}
catch(int i) {
cout << "Age( " << age << " ) is not valid. ";
}
}
int main() {
string name;
int age;
try
{
cout << "Your name ? ";
cin >> name;
if (name.length() > 10) throw name;
cout << "Your age ? ";
cin >> age;
if (age < 0 || age >= 150)throw age;
cout << "Age: " << age;
}
catch (int i) {
cout << "Age( " << i << " ) is not valid. ";
}
catch (string s) {
cout << "Name( " << s << " ) is not valid. ";
}
}
void Swap(int& a, int& b) {
int tmp;
tmp = a;
a = b;
b = tmp;
}
void Swap(double& a, double& b) {
double tmp;
tmp = a;
a = b;
b = tmp;
}
===========================
기존에는 위 처럼 정의했다. Template를 사용하면 밑 처럼 간략하게 정의할 수 있다.
template<typename T>
void Swap(T & a, T & b) {
T tmp;
tmp = a;
a = b;
b = tmp;
}
int main() {
int a = 10, b = 5;
Swap<int>(a, b);
cout << “a=“ << a << “ b=“ <<b;
double c = 1.0, d = 2.0;
Swap<double>(c, d);
cout << “\nc=“ << c << “ d=“ <<d;
}
----
a=5b=10
c=2d=1
#include
<iostream>
template <typenameT>
class Point { T x; T y;
public:
Point(
T xx = 0,
T yy = 0) : x(xx), y(yy) { }
T getX() { return x; } T getY() { return y; }
};
int main() {
Point<int> pt_i{ 1, 2 };
cout << pt_i.getX() << endl;
Point<double> pt_d{ 1.2, 3.4 };
cout << pt_d.getX() << endl
;
}
▪ int iNum1 = 10.5; //Copy initialization
▪ int iNum2(30.5); // Direct initialization
▪ int iNum3{ 20.5 }; // Uniform initialization : Error
float f = 1.5;
int i = 10;
int h = f+i;
float x = 1.5;
int sum = (int)x + 1;
cout << sum;
const double x{10};
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.
if (condition)
statement;
else if (condition) // Optional
statement;
else if (condition) // Optional
statement;
else // Optional
statement;
----
Enter your score :
60
60Points: Grade C
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 (variable or expression)
{
case constant1 :
statements;
break;
case constant2 :
statements;
default :
statements;
}
#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;
}
for ( ① ; ② ; ③ ) statement;
① : initial statement – can be omitted
② : termination condition – can be omitted
③ : statement after each iteration – can be omitted
// 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
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 (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 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);
goto 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;
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 );
}
① Macro Constant
#define PI 3.14
② Macro Function
#define NAME(Parameter) Replacement
#define Multiply(x, y) x*y
int a = Multiply(3, 2);
// 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;
}
• 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
• 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
#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
#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
#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
#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
#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에 변수 넣기 위해 동적할당
#include <iostream>
using namespace std;
int main() {
int* ptr;
while (1) {
cout << "Hello";
ptr = new int[10000];
// delete[] ptr; 넣어주면 문제 해결!
}
return 0;
}
for (int i = 0; i < 3; i++) {
delete[] p_arr[i];
}
delete[] p_arr;
#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
#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이 잘 동작하는 걸 확인할 수 있다.
int* f1() {
int iNums[3]{ 1,2,3 };
return iNums; // adress of array
}
int main(){
int* pNums = f1();
cout << *pNums << endl;
return 0;
}
#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;
}
#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;
}
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;
#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
#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;
}
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;
#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;
}
----
#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