HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
장지원 페이지/
[Notion] :
[Notion] :
/
과목 페이지 생성
과목 페이지 생성
/
객체지향 프로그래밍

객체지향 프로그래밍


 
📎
<Final>

1. Polymorphism (1)

lecture 9 - Polymorphism (1) - release.pdf
406.4KB
 

Function/Construct overloading

 
같은 이름을 가진 함수를 각각 다른 자료형의 return, parameter를 갖게 구현하는 것을 function overloading이라고 한다.
 
construct overloading도 마찬가지이다.
 

Operator overloading

 
<매우 도움이 된다>
eun-seong.github.io
C++에서 제공하는 기본 타입이 아닌 클래스 타입에도 연산자를 사용할 수 있게 하는 문법입니다.
eun-seong.github.io
https://eun-seong.github.io/TIL/posts/C++/syntex_operator_overloading
 
class도 하나의 자료형이다.
 
class A가 있고, A의 instance로 a1과 a2가 있다고 가정해보자.
그렇다면 a1+a2는 어떻게 정의할까?
operator에 대해서도 overloading이 구현되어야 할 것이다.
(한글로는 연산자 중복이라고 한다고 한다)
 
지금부터 이것에 대해 알아볼 것이다.
 
notion image
연산자는 binary, postfix, prefix 세 가지의 형태로 나눌 수 있다.
이에 따라 argument가 바뀌게 된다. 잘 구분해서 사용하도록 하자.
기존 쓰임을 거의 비슷하게 따르게 되니 기존에 어떻게 쓰였는지를 바탕으로 참고하면 될 것 같다.
 
(참고) friend 함수
class 내부에 정의되어서 class 내의 모든 member 함수, 변수에 접근 가능하지만 class의 member 함수가 아닌 함수를 의미한다. (따라서 상속 되지 않는다)
 
complex class에서의 operator overloading에 대해서 알아보자.
 
complex constructor의 coverloading은 밑과 같이 정의되어있는 상태이다.
 
 
(+) operator
 
먼저 complex instance의 member variable을 불러와서 작업을 수행하는 방법이다. 다음과 같이 정의할 수 있다.
 
 
주의 : 반환 값은 result instance이다. 이러한 반환 값이 operator 마다 다를 수 있으니 유의해보도록 하자.
 
다음은 int, 또는 double 값을 받아와 작업을 수행하는 방법이다. 다음과 같이 정의할 수 있다.
 
 
두 가지 방법으로 구현이 가능하다. static_cast<int>는 double을 int로 바꿔주는 함수이다. 첫 번째 방법에서는 반환 값이 없는 constructor를 호출하고 있고, 두 번째 방법에서는 반환 값이 있는 constructor를 호출하고 있다.
 
마지막으로 왼쪽에 객체가 아닌 int나 double이 오는 경우이다.
교수님 설명 : argument 왼쪽에 객체가 아닌 것이 오면 member function을 사용할 수 없기 때문에 friend 함수를 사용해야 한다. (교수님이 이거 그냥 외우라고 하셨다,, 교수님도 모르시는게 아닐까)
블로그 설명 : argument에 class instance 아닌 것이 오면 member를 이용할 수 없으므로 friend 함수를 사용해야 한다.
난 둘 다 이해 안 간다.. 나중에 차근차근 이해해보자..
→ 남수민 피셜 : this가 안들어가니까 friend로 정의한다. 이걸로 우선 이해하고 넘어가기,,
다음과 같이 정의할 수 있다.
 
 
반환 값은 마찬가지로 result instance이다.
 
(== / +=) operator
 
== 연산자는 반환 값이 bool이고, += 연산자는 반환 값이 존재하지 않는다.
 
 
반환 값이 어떤 형태인지 잘 알아두도록 하자.
(++ / prefix, postfix) operator
 
prefix ++는 return 값을 자기 자신 참조 값으로 넘겨준다. postfix는 자기 자신을 copy 했던 것을 넘겨준다.
 
postfix가 copy value를 넘겨주는 이유는 무엇일까?
postfix는 연산이 끝난 이후에 값을 증가 시킨다. 따라서 기존 값을 전달 할 필요가 있다. 이를 위해 기존 값을 copy해 두었다가 return 시키는 것이다.
 
postfix의 argument인 dummy는 그냥 규칙이다. 아무 역할도 하지 않는다.
 

2. Polymorphism (2)

lecture 10 - Polymorphism (2) - release.pdf
285.5KB
 

Operator overloading (2)

 
저번에 다룬 operator 뿐만 아니라 다양한 operator들의 overloading이 가능하다.
 
notion image
 
이번 PPT에서는 <<, [], = 연산자의 overload를 해볼 것이다.
 
(<<) operator
 
<<도 전 PPT의 +(double, pointer)에서와 마찬가지로 자기 객체(this)를 이용하지 않았기 때문에 friend로 정의하였다.
 
 
위와 같이 정의하면 원하는 형식의 output을 정의해서 사용할 수 있다.
 
11번째 줄의 o는 cout을 의미한다. (cout은 ostream의 object이다)
([]) operator
 
[]는 primaitive type에는 적용되어있다. 하지만 우리가 정의한 (밑에서의 vector class) class에서는 정의되어있지 않다. 따라서 이도 마찬가지로 overload 해주어야 한다.
 
코드는 다음과 같다.
 
 
 
 
notion image
 
L-value : 직접적으로 access할 수 있는 값
R-value : complier에 의해 임시적으로 할당된 memory. 우리 코드에서는 직접적으로 access할 수 없다.
In assignment “=” : L-value는 LHS, RHS 어디든 올 수 있다. R-value는 RHS에만 올 수 있다.
 
b+c는 R-value이다. b, c 개별로는 각각 L-value이다.
++a는 prefix이기 때문에 접근 가능(L-value)하다.
a++는 post이기 때문에 접근 불가능(R-value)하다. copy되고 사라지기 때문이다.
 
(==) operator
 
코드는 다음과 같다.
지금 이해하고 싶지 않으니 다음에 보기,,
 
 

Std::string

 
notion image
string class를 이용하면 더 다양하게 string을 다룰 수 있다.
 
string class에는 다양한 operator들이 overloading 되어있다. 우리는 그것들을 활용하면 된다.
 
  • + : string을 합치는 역할을 해준다
  • += : 마찬가지의 역할을 해준다.
  • == : 두 string 같다면 true, 틀리다면 false를 출력하는 역할을 해준다.
  • <,>,≤,≥ : boolalpha와 주로 같이 사용된다. boolalpha는 참이라면 true, 틀리다면 false를 출력해준다.
  • [] : 해당 index에 있는 value를 출력해준다. (index는 0부터 시작한다)
  • substr(a, b) : 2부터 시작해서 4개의 string 가져온다. (ex. ABCDEFG라면 CDEF를 출력해준다)
  • find / rfind : find는 일치하는 가장 왼쪽 값의 시작 index를 return해준다. rfind는 오른쪽 값의 시작 index를 return 해준다.
 
 
 

3. Inheritance (1)

lecture 11 - Inheritance (1) - lecture - release.pdf
452.6KB
 

OOP

 
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일 때도 가능은 하다.
 
notion image
 
위와 같이 공통된 부분은 상속을 통해서 구현하고 이후에는 각각의 class마다 필요한 것들을 specialized 시킨다. (중복 구현을 안하게 해준다!)
 
문법은 다음과 같다. ‘자식 클래스의 이름’ : ‘ 상속 타입 ‘ ‘부모 클래스’
 
notion image
 
inheritance type(상속 타입)에 따라 child class의 member access 범위가 달라진다.
 
참고 : protected는 자식 class 내에서 사용 가능하고, 자식 class 내에서 private 취급을 받는다.
private는 외부 뿐만 아니라 자식 class에서도 사용 불가능하다.
 
notion image
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을 진행 하였다.
 
아래 코드에서 에러가 발생하는 이유는 무엇일까?
 
//cout << p_arr[0]->GetStudentName() << endl; //Error //cout << p_arr[1]->GetTeacherID() << endl; //Error
 
p_arr[0]은 upcasting을 했기 때문에 type이 Person형 pointer로 바뀌었을 것이다. 따라서 Studernt와 Teacher에 specialized된 member 함수에 접근하지 못한다.
 
<up casting을 하는 이유>
 
코드의 ‘재사용성을 높이기 위해서’라고 한다. 나중에 자세히 알아보자.
[C++] 업 캐스팅(up casting) & 다운 캐스팅(down casting)
포인터는 왜쓰고 업&다운 캐스팅은 뭘까요? 설명에 앞서 저는 업캐스팅을 이해하려면 포인터부터 알아야 한다고 생각합니다. 해서 포인터부터 설명을 드리겠습니다. 학창시절 포인터는 C에서 배웠지만 그냥 해당 타입의 메모리 주소를 저장 및 참조(접근)하는 그런건가 보다~ 하고 넘겼었고 업&다운 캐스팅은 왜 쓰는지 도무지 감이 안잡혔었습니다. 그래서 서적이나 블로그 등 많이 찾아봤지만 이 질문에 대한 명확한 답을 찾기가 어려웠습니다. 많은 분들이 저와 같은 고민을 했으리라 생각합 아니 하고 싶습니다^^.. 때문에 제가 아는 선에서 여러분에게 최대한의 이해를 돕고자 포스팅을 하게 되었습니다. 그리고 "C++ 클래스에 대한 이해 - 다형성"를 읽는데 도움이 되실겁니다. 서두가 길었습니다. 시작하시죠 먼저 포인터란 주..
[C++] 업 캐스팅(up casting) & 다운 캐스팅(down casting)
https://rehtorb-algorithm.tistory.com/8
[C++] 업 캐스팅(up casting) & 다운 캐스팅(down casting)

Type Conversion (2) - down casting

 
down casting : 부모 클래스(base class)의 reference 혹은 pointer를 자식 클래스(derived)로 바꾸는 것이다. (주의 : down casting은 up casting 보다 불안정하다)
 
예시를 들어 설명하자면
Person is a student → Student is a person는 괜찮지만
Student is a person → Person is a student는 살짝 어색하다.
 
 
Derived * pD = (Derived *) pB;이러한 방식으로 dow casting을 할 수 있다.
 
 
(Student*)p_arr[0], (Teacher*)p_arr[0], (Teacher*)p_arr[1], (Student*)p_arr[1] 위 코드에서는 이 부분을 통해 downcasting을 진행 하였다.
 
위 코드에서는 error가 발생하지 않는다. 그러나 person class가 teacher, student 모두로 downcast 되는 것을 확인할 수 있다. 이러한 점 때문에 down casting을 불안정하다고 하는ㄴ 것이다.

Constructor

 
기본적으로 base의 constructor이 불려진 다음에 chlid의 constructor이 불려진다.
 
 
위는 Implicit case이다. 아무런 정보가 없다면, default constructor이 불려진다.
 
 
위는 Explicit case이다. 구체적인 정보가 있다면, specified된 constructor가 불리어진다.
 
다음은 예시 코드이다.
 
 
 
 
 

Destructor

 
[여기 다시 하기]
 
Constructor와 반대 이다. child destructor가 불려진 다음에 base의 destructor가 불려진다.
 
여기서는 한 가지 문제가 발생한다.
 
 
만약 upcast 이후에 delete를 한다고 생각해보자. 이 때는 person의 destructor만 불려질 것이다. 만약 student에서 지워져야 할 내용이 있다면 곤란해진다. 이는 뒤에서 다룰 virtual keyword를 이용해서 해결할 수 있다.
 
order of constructor/destructor calls 이다.
 
 
 

Multiple Inheritance

 
여러 번의 상속을 할 수도 있다.
 
이 때는 밑과 같은 문제가 발생할 수 있으니 주의해야 한다.
 
notion image
 

4. Inheritance (2)

lecture 12 - Inheritance (2) - release.pdf
364.1KB
 

Overloading vs Overriding

 
상속에서의 다형성에 대해 다루어볼 것이다.
 
Overloading과 Overriding에 대한 설명은 다음과 같다.
 
notion image
 
overloading : 다른 타입의 파라미터를 사용하고 싶을 때 사용한다. / Inheritance가 없을 때도 사용할 수 있다. / 같은 함수에 대해 signiture를 바꾸며 다양한 정의를 할 수 있다.
 
overriding : 자식 클래스에서 함수의 기능을 바꾸고 싶을 때 사용한다. / 상속 되었을 때만 사용할 수 있다. / 같은 signiture를 가지는 것들을 각각 다른 기능을 하도록 바꾼다.
 
밑은 각각 overloading과 overriding의 예시이다.
 
  • overloading example
 
notion image
  • overriding example
notion image
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 내에 실행된다.
 
notion image
정적 바인딩(Static binding) vs. 동적 바인딩(Dynamic binding)
* Binding - 프로그램 구성 요소의 성격을 결정해주는 것ex ) 변수의 데이터 타입이 무엇인지 정해지는 것 종류정적 바인딩(Static binding) 동적 바인딩(Dynamic binding) 정의 컴파일 시간에 성격이 결정되는 것 실행 시간(runtime)에 성격이 결정되는 것 예시C언어 컴파일 시간에 변수의 데이터 타입이 결정Python(Interpreter 언어) 런타임에 값에 따라 변수의 데이터 타입이 결정 장단점 컴파일 시간에 많은 정보가 결정되므로 실행 효율↑ 런타임에 자유롭게 성격이 바뀌므로 적응성↑ * 함수의 바인딩- 함수를 만들어 컴파일을 하면 각각의 코드가 메모리 어딘가에 저장된다.그리고 함수를 호출하는 부분에는 그 함수가 저장된 메모리 번지수(주소값)이 저장된다. 프로그램 실..
정적 바인딩(Static binding) vs. 동적 바인딩(Dynamic binding)
https://secretroute.tistory.com/entry/140819
정적 바인딩(Static binding) vs. 동적 바인딩(Dynamic binding)
 
다음은 virtual 함수를 사용한 예시이다.
 
 
up casting 하기 전과 후가 같게 동작함을 확인할 수 있다.
 
(질문 : 그럼 derived class를 그냥 사용하면 되지 왜 굳이 up casting을 거칠까? 인터넷에 따르면 다형성을 통해 코드의 재사용성을 높이기 위해서라고 한다. 이에 대해서는 나중에 다시 알아보자)
 
virtual의 개념은 deconstructor에서도 적용된다, 예시는 다음과 같다. up casting 이후에 본래의 deconstructor이 불려지지 않는 문제를 해결할 수 있다.
 
 
 

Pure Virtual Function

 
notion image
 
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이다.
 
notion image
final function : 마지막 function이다. 자식 class에서는 더 이상 이 함수를 가질 수 없음을 의미한다.
 
notion image
 
final class : 더 이상 자식을 가질 수 없는 class이다.
 
notion image
 
override keyword : 함수가 override되었는지 안되었는지 확인해주는 키워드이다.
 
만약 override가 안되었다면 error를 출력시킨다. 따라서 override가 된 함수에서만 사용해야한다. 단순히 override가 된 함수인지 아닌지 시각적으로 빨리 판단하기 위해서 사용한다.
 
notion image
 

5. Exceptional Handling

lecture 13 - Exception - release.pdf
569.9KB
 

Type of Errors

 
c++에서 발생하는 에러의 종류이다. 크게 세 가지로 나눌 수 있다.
 
notion image
 
Syntax error는 문법 오류이다. Run-time error는 실행 이후에 ‘어떠한 이유’로 생기는 에러이다 (내가 C++를 다룰 때 가장 발생하는 오류인 것 같다. 이를 잘 다루는게 중요하다. 이 오류는 에러를 출력시키지 않을 때도 있다). Semantic error 또는 Logical error는 의도에 맞지 않게 결과가 출력되는 오류를 의미한다.
 
예시 사진은 다음과 같다.
 
notion image
 
 

Try-Throw-Catch

 
위 error말고도 handling 해주어야 할 것들이 있다. (텀 프로젝트 과제로 주어졌던 ATM에서 handling 했던 것 처럼)
 
notion image
 
만약 위 사항들을 if else문으로 처리한다면 코드가 매우 복잡해지고, 길어진다는 단점이 있다.
 
notion image
 
 
  • Try-Throw-Catch
Try-Throw-Catch 문법을 이용하면 이러한 것들을 좀 더 손쉽게 handling할 수 있다.
 
문법은 다음과 같다. try 안에서 error 사항을 잡고, catch 부분에서 error 사항을 처리하면 된다.
 
notion image
 
 
  • multi-Try-Throw-Catch
다중 Try-Throw-Catch문은 다음과 같이 정의하면 된다. throw한 것과 일치하는 파라미터 타입을 가지고 있는 catch문에 들어가서 error 처리를 진행한다.
 
catch(…)는 위 throw가 위의 어떤 catch문에도 걸리지 않았을 때 실행된다. default 값으로 사용한다고 생각해도 된다. 만약 이것도 없다면 computer 내의 default를 수행한다. ( “terminate called after throwing an instance of ‘int’”라는 에러를 출력하며 종료할 것이다)
 
notion image
 
  • nested trying and catching
밑처럼 복잡하게 얽혀있을 때 어느 부분에서 try-catch가 일어나는지 잘 살펴보아야 한다.
 
notion image
 
 

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, 스택 풀기 라고 한다)
 
[ C++ ] Stack Unwinding(스택 풀기)
이전 포스팅에서 함수 내에서 throw를 던진 경우에 그 함수를 호출한 영역으로 돌아가서 예외처리를 하였다. 이렇듯, 예외처리에 대한 책임은 throw가 발생한 함수를 호출한 영역으로 넘어가게 된다. 그리고 호출한 영역으로 '예외' 데이터를 전달하면, 해당 함수는 더 이상 실행되지 않고 종료가 된다. 이렇게 함수 내에서 예외가 처리되지 않아서, 함수를 호출한 영역으로 예외 데이터가 전달되는 현상을 가리켜 'Stack Unwinding(스택 풀기)'라고 한다. 위의 함수 호출 순서는 다음과 같다. main -> One -> Two -> Three ( 스택이 쌓이는 방향 ) 그리고 Three에서 예외를 던졌다. 그래서 다시 Three -> Two -> One -> main ( '예외' 데이터가 전달되면서 스..
[ C++ ] Stack Unwinding(스택 풀기)
https://musket-ade.tistory.com/entry/C-Stack-Unwinding스택-풀기
[ C++ ] Stack Unwinding(스택 풀기)
 
예시는 다음과 같다.
 
 
  • MyException
아래와 같이 catch의 파라미터를 class의 포인터의 값으로 해서 사용할 수도 있다.
 
 

Standard Exception Class / Etc(2)…

 
c++에는 이미 이러한 Exception을 handling하기 위한 class들이 준비되어있다.
 
notion image
 
 
  • Example - Exception Class
밑은 이미 구현되어 있는 exception class를 이용해서 error를 handling한 예시이다.throw가 쓰이지 않았지만 내부적으로 throw해준다.
 
 
  • no except
no except는 예외를 던지지 않는 함수를 의미한다.
 
(시험에는 나오지 않은 것 같다. 나중에 시간 남으면 한 번 읽어보자)
14. 예외를 방출하지 않을 함수는 noexcept로 선언하라.
- noexpcet는 함수의 인터페이스의 일부이다. 이는 호출자가 noexcept 여부에 의존할 수 있음을 뜻한다. C++98에서 예외 지정은 다소 변덕 스러운 야수였다. 함수 구현이 바뀌면 예외 지정도 바뀔 가능성이 생겼고, 기존의 예외 지정에 의존하던 클라이언트 코드는 깨질 수 있었다. 게다가, 대체로 컴파일러는 함수구현과 게다가, 대체로 컴파일러는 함수 구현과 예외 지정, 그리고 클라이언트 코드 사이의 일관성 유지에 아무런 도움도 주지 않았다. 그래서 대부분의 프로그래머는 결국 C++98의 예외 지정이 득보다 실이 크다고 판단하게 되었다. C++11 제정 과정에서, 함수의 예외 방출 행동에 관해 정말로 의미 있는 정보는 함수가 예외를 하나라도 던질 수 있는지 아니면 절대로 던지지 않는지의 여부라는 ..
14. 예외를 방출하지 않을 함수는 noexcept로 선언하라.
https://lakanto.tistory.com/35
14. 예외를 방출하지 않을 함수는 noexcept로 선언하라.
 
notion image
 
  • Uncatched Exception
default를 설정해주기 위한 방법 중 하나이다. catch(…) 또는 밑의 방법을 사용하면 된다.
 
notion image
 

6. Template

lecture 14 - Template - release.pdf
189.6KB
 

Templates

 
notion image
 
위 사진을 살펴보자. 위는 overloading과 overriding을 함께 사용했을 때 나타나는 예시이다.
overloading만을 사용했다면 hello()가 잘 출력되었을 것이다. 하지만 위해서는 overriding을 하기 위해서 virtual keyword를 사용하였다. 때문에 자식 클래스에서 파라미터가 없는 hello 함수를 출력했을 때 오류가 생긴다. 이는 Option1 또는 Option2를 통해서 해결할 수 있다.
 
Template는 이를 해결해 줄 수 있다. Templete를 이용하면 같은 기능을 하는 함수를 굳이 여러 파라미터에 대해 정의해주지 않아도 되고, 따라서 위와 같은 실수도 예방할 수 있게 된다.
 
문법은 다음과 같다. 맨 위에 template<typename T>를 적고, 사용할 때 Typename<Type> 으로 타입을 정해서 사용하면 된다.
 
notion image
 
다음은 함수에 대한 Template사용 예시이다.
 
class에서도 template를 활용할 수 있다. 예시는 다음과 같다.
 
 
변수 두 개를 이용할 수도 있다. 예시는 다음과 같다.
 
 
변수 두 개를 사용하되 하나를 default 값으로 설정할 수도 있다. 예시는 다음과 같다.
(주의 : default는 무조건 두 번째 변수만 가능하다. 만약 T1을 default로 설정하고 T2를 default로 설정하지 않으면 오류가 발생하게 된다)
 
 
type 대신 value를 넣어줄 수도 있다. 예시는 다음과 같다.
 
 
template를 specialization할 수도 있다. 예시는 다음과 같다.
 
 
char + char는 불가능하기 때문에 const char* Add 함수를 따로 만들어준 것이다.
그럼 다른 기능을 제공할거면 template 대신 overloading을 쓰는게 더 편하지 않을까?
챗지피티의 말로는 template은 컴파일 타임에 동작하기 때문에 더 효율적인 동작이 가능하다고 한다.
 

7. STL(1)

lecture 15 - STL (1) - release.pdf
279.0KB
 

STL

 
STL Standard template library의 약자로 우리에게 제공되는 라이브러리이다.
 
algorithm, container(컨테이너가 무엇인지는 뒤에서 다룰 것이다), functions, iterators 4가지 요소를 우리에게 제공해준다.
 
container란 object를 담을 수 있는 통을 의미한다. 이는 자료형을 섞어서 담을 수 있는 general한 array이다. 파이썬의 list와 비슷하다고 이해해도 될 것 같다. 어떤 type도 담을 수 있어야 하므로 templete로 구현한다.
 
STL이 제공해주는 것을 다이어그램으로 표현하면 다음과 같다.
 
notion image
 

Container

 
특징은 위에서 언급했듯이 다음과 같다.
  1. object의 holder이다.
  1. templates으로 구현한다.
  1. 메모리 관리와 접근은 iterators로 한다.
 
다음은 컨테이너의 종류이다.
  • sequential container
    • 순차적 접근이 가능한 container이다. (ex. vector, list, foward_list…)
  • associative container
    • sort된 data를 바탕으로 빠르게 search할 수 있는 container이다. (ex. set, multiset, map…)
  • unordered container
    • hashed된 data를 바탕으로 빠르게 search할 수 있는 container이다. (ex. unordered_set, unordered_map…)
 
다음은 우리가 사용할 vector를 포함하는 Sequential container에 대한 자세한 설명이다.
 
notion image
 
push_back은 append 역할을 하는 함수이다. size는 len 역할을 하는 함수이다.
 
notion image
 
insert를 하면 linked list 방식으로 element가 삽입된다.
 
notion image
 

Container - Vector

 
notion image
위와 같은 종류의 constructor가 있다. template를 이용했기 때문에 앞에 <type>을 붙여서 타입을 정의해준다.
 
notion image
 
vertor에는 여러 method들이 있다.
at : arr[i]와 유사한 역할을 한다. (arr[i]를 사용해도 된다)
back/front : 마지막/앞을 구경한다.
capacity : byte 수를 세준다. (이건 사용을 지양하자)
size: len() 역할을 한다.
empty : empty인지 아닌지 알려준다.
shrink_to_fit : un use memory를 삭제해준다.
 
push_back : 맨 뒤에 원소를 삽입해준다.
pop_back : 맨 뒤에껄 뺀다.
erase : 모두 지운다.
operator : at과 비슷한 역할을 한다.
clear : erase와 비슷한 역할을 한다.
insert(위치, 원소) : 원하는 위치에 원소를 삽입한다.
 
begin() : 시작
end() : 끝 (end-1까지 돈다. 파이썬과 비슷하다)
 
색으로 표시해 둔 것들은 iterator가 필요한 것들이다.
 
다음은 vector의 사용 예시이다.
 
 

Iterator

 
포인터와 바슷하게 동작한다.
각 요소에 대한 접근을 제공하는 ‘객체’이다.
 
다음 예시를 보면 포인터와 비슷하게 동작함을 확인할 수 있다. vector<int>::iterator의 형태로 선언하고 사용해야 한다.
 
notion image
 
예제 코드이다.
 
 
for문 안에 밑의 코드를 삽입하여 동작 시킬 수도 있다.
 
 
for문을 위의 방식 말고도 다르게 정의할 수 있다.
 
 
 

8. STL(2)

lecture 16 - STL (2) - release.pdf
801.3KB
 

Container - Maps

 
Map을 포함하고 있는 associative container에 관한 설명이다.
파이썬의 dictionary와 같은 형태를 가지고 있다. 이 특성을 활용해서 좀 더 빠른 탐색이 가능하도록 하였다.
 
notion image
 
다음과 같은 형태로 사용할 수 있다.
 
notion image
 
지금까지는 vector와 map에 대해서 알아보았다. 이 외에도 다양한 container들이 있다. 밑 표는 각 container가 수행하는 작업에 대한 시간 복잡도를 나타낸 것이다.
 
notion image

Algorithms

 
STL에서 제공하는 알고리즘을 가져다 쓸 수도 있다.
 
다음 표를 참고해서 사용하면된다.
PPT에 다양한 예제들이 있으니 참고하도록 하자.
 
notion image
 
 
 

 
📎
<midterm>

1. C++ Basic

lecture 2 - C++ Basic - release.pdf
485.2KB
 

Stored Program system with Von Neumann Architecture

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

Variables Type

 

- int type

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

- float type

 
notion image
밑 코드를 출력해보면 어떤 결과가 나올까?
 
 
첫 번째 값에서는 int를 출력하기 때문에 .0이 버려져 나온다. 마지막 값이 이상하게 출력되는 걸 확인할 수 있는데 이는 information loss 때문이다.
cout은 defalt precision of 6을 가지고 있다. 하지만 우리는 6자리까지 출력하라고 했으므로 loss가 일어난 것이다.
 

- char type

 
character 변수 type이다.
 

- variable size

 
sizeof 함수로 variable의 memory size를 알 수 있다.
notion image
여기서 출력되는 숫자는 compiler의 차이로 인해서 컴퓨터마다 다를 수 있다.
 

- auto

 
auto로도 type지정을 할 수 있다. 컴퓨터가 자동으로 type을 지정해 준다. 단 효율 문제는 잃어버릴 수 있다.
 
notion image
 

- String and Character literals

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

Etc…

 

- Variable initialization

 
변수 초기화는 밑과 같이 진행한다.
 
 
Error가 생기는 이유는 int형 변수에 double형 값을 넣으려고 했기 때문이다. iNum1과 iNum2에서는 10, 30을 출력해 줄 것이다 (컴파일러마다 출력 값은 다를 수 있다). 따라서 이 문제를 방지하고 싶다면 {}를 쓰지 않으면 된다.
 

- Implicit type conversion

 
type이 mismatch 된다면 컴파일러는 implicit conversion를 진행한다. 이 때 information loss가 일어날 수도 있다.
 
notion image
 
위의 경우에서는 information loss가 일어나지 않지만, 큰→작은 으로 이동하면 information loss가 일어난다.
 
 
밑의 코드에서는 h = 11이 출력된다. 11.5가 출력되어야 하는데, information loss가 일어난 것이다.
 

- Explicit type conversion

 
프로그래머 의도와 다르게 conversion 되는 걸 막기 위해서 명시적으로 type conversion을 해주는 경우도 있다.
 
밑의 코드 처럼 () 괄호안에 자료형을 적으면 된다.
 
 

- Constant

 
어떤 값이 추후에 바뀌지 않아야 하는 경우 사용한다.
 
밑 코드 처럼 작성하면 된다. 이 때 x를 수정하려고 하면 error가 생긴다.
 
 

- Basic Oprtators

 
다음과 같은 기본 연산자들이 존재한다.
 
notion image
notion image
notion image
 

2. Control Flow

lecture 3 - Flow Control - release.pdf
359.3KB
 

Standard input/output

 
cout, cin, <<을 활용해서 input과 output을 구현할 수 있다.
 
 
\n, \t, ‘ ‘등 여러 문자열을 활용해서 출력 형태를 바꿀 수 있다.
 

Conditional statement

 

- if/else if/else

 
조건문이다. 파이썬과 거의 비슷한 문법을 가지고 있다.
 
 
모든 statement를 {}로 묶어주는 게 좋다. 한 줄 statement는 묶어줄 필요 없지만 이걸 묶지 않으면 상당히 헷갈린다.
 

- Ternary operator

 
Ternary operator를 사용해서 if문을 더 간단하게 할 수도 있다.
Ternary operator는 밑과 같은 문법으로 이루어져 있다.
 
 
이를 이용하면 코드를 더 간결하게 나타낼 수 있다.
 
 

- Switch

 
Switch-case문은 “variable이 constant x일 때 statement를 수행하라”라는 의미를 가지고 있는 명령문이다.
 
 
다음은 switch case문과 if문의 비교이다. 그냥 if문을 쓰는게 좋을 것 같다. switch-case문에서 주의해야 할 점은 break를 꼭 넣어주어야 한다는 것이다. 이걸 안넣어주면 뒤에꺼도 계속 실행된다.
 
 

Loop statement

 

- for loop

 
파이썬의 for문과 비슷한 문법을 가지고 있다. 주의해야 할 점은 loop에 활용할 변수도 정의해야 하고, 전위, 후위 연산자도 잘 고려해야 한다는 것이다.
 
 
if문을 사용했을 때보다 길이가 줄어든 것을 확인할 수 있다.
 
notion image
 
break과 continue 또한 사용할 수 있다. break를 사용하면 중간에 끊긴다. continue를 사용하면 그 순서의 문자를 건너뛰고 출력시켜 준다.
 
 
range-based for loop도 있다.
아래 세 코드가 모두 같은 기능을 수행한다. 참고하도록 하자.
 
 

- While loop

 
while문의 문법은 다음과 같다.
 
 
 

- Do~While loop

 
do-while문의 문법은 다음과 같다.
 
 
 
위의 while문 예시 코드와 비교해보면 조건 - 수행의 순서가 바뀐 것을 확인할 수 있다. do-while문의 가장 큰 특징은 조건에 상관없이 한 번은 무조건 실행된다는 점이다.
 

- Goto Statement

 
Goto statement의 코드는 다음과 같다.
 
 
Label 조건이 맞으면 Label로 이동하라는 의미를 가진다.
 
 
 

3. Function

lecture 4 - Function - release.pdf
366.9KB
 

Function

 

- 함수 사용 이유

 
함수를 사용하는 이유는 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와 같다.
 
notion image
 
위의 그림과 같이 사용할 수 있다.
 
(여기서 int Multiply (int, int =1)이 뭘 의미하는지 헷갈렸음. 이 뒤에 int=1은 default 값 정의해 준거라고 생각하면 됨!)
 

- Preprocessor

 
preprocess는 compile전에 수행되는 과정이다.
앞에 #이 붙은 건 모두 preprocessing 된다.
 
#include
#define
#ifndef DEFINE
#endif
등이 preprocessing 된다.
 

- Namespace

 
namespace는 큰 프로젝트에서 name이 겹치는 걸 막아준다.
 

4. Array and Pointer (1)

lecture 5 - Array and Pointer (1) - release.pdf
552.3KB
 

Basic of Computer Architecture

 

- Computer architecture

 
notion image
 
 
control unit과 memory 사이에서는 메모리 주소를 주고 받으며 data를 주고 받는다.
notion image
 
notion image
 
대부분의 경우에 우리는 메모리가 ‘어디에’ 할당되었는지 까지는 알 필요가 없다.
 

- Memory hierarchy

 
뭐.. 이런 것도 있다.. 정도로만 알아두자.
 
notion image
notion image
 

- Stack, Heap, and Address

 
notion image
 
우리가 알아야 할 가장 중요한 구조이다.
 
stack에는 compile time 동안 실행되는 data들이 저장된다.
heap에는 program이 run 되는 동안 실행되는 data들이 저장된다.
code and some data에는 전역 변수나 static 변수, 코드 등이 저장된다.
 

Pointer

 

- Pointer 기본 개념

 
Variable : 유저가 값은 저장한 주소에 붙이는 이름
Pointer : 값의 주소를 가지고 있는 변수
 
즉 Pointer도 하나의 변수라고 이해하면 좋다.
 
다음은 Pointer를 사용할 때 가장 많이 쓰는 두 가지 표현이다.
  1. Address-of (&) : 변수가 있는 주소를 확인할 때 사용한다.
  1. Dereference (*) : 특정 주소에 있는 값을 확인할 때 사용한다. 또는 특정 값에 접근할 때 사용한다.
 
밑의 코드처럼 사용할 수 있다.
 
 

- Pointer 사용

 
선언은 앞에 별을 붙여서 할 수 있다.
 
 
initialization도 기존 변수처럼 하면 된다.
 
 
사용은 밑의 코드와 같이 한다. 중요하게 봐야 할 점은 dereferencing으로 주소에 있는 값에 접근한다는 점이다.
 
 
diagram으로 보면 다음과 같다.
 
notion image
 

- Pointer의 size

 
 
이런식으로 입력해주면 포인터의 크기를 확인할 수 있다.
 
컴퓨터마다 다를 수 있지만 교수님 컴퓨터 기준 char : 1, int : 4, char* : 4, int* : 4 (byte)이다.
 
간단한 예시 코드이다. double pointer는 pointer의 주소를 저장할 수 있다. HW1에 세 번째 문제를 보면서 더 자세히 익히도록 하자.
 

Why use Pointer?

 
Swap 함수를 통해 Point의 중요성을 알아볼 수 있다.
Swap1은 call by value를 이용한 함수이고, Swap2는 call by reference를 이용한 함수이다.
 
 
위 코드를 실행해 보면 swap1함수에서는 값들이 swap되지 않을 것을 확인할 수 있다.
이는 swap1함수가 종료되며 stack에 있는 값들이 모두 deallocated되기 때문에 발생하는 문제이다.
이 문제는 static 변수를 사용해도 해결할 수 없을 것이다. (뭐 복잡하게 구현하면 어찌어찌 되겠지만 위 상태에서는 불가능할 것이다 → 교수님 말씀 : static의 lifetime을 고려하면 static 이용해도 swap할 수 없을 것이다)
 
하지만 swap2 함수에서는 값이 바뀐 것을 확인할 수 있다. 이는 Pointer를 이용하였기 때문이다.
밑의 그림과 같이 주소를 이용해서 값을 바꿔주는 call by reference 방식을 이용했기 때문에 값이 바뀌어서 출력되는 걸 확인할 수 있다.
 
notion image

Array

 

- 배열이란?

 
같은 type의 많은 data들이 들어있는 자료형이다.
파이썬의 list에서는 다른 type이 함께 있을 수 있지만 배열을 그렇게 할 수 없다. 배열에는 무조건 같은 type의 data만 들어있어야 한다.
 
배열의 정의는 자료형 뒤에 []만 붙여주면 된다.
 
 
배열의 사용과 초기화 방법은 밑과 같다.
 
 

- char[] (String: not bulit-in type)

 
char type이 들어있는 자료형은 밑 코드와 같이 정의할 수 있다.
 
 
하지만 위 코드를 출력해보면 John 뒤에 가비지 값이 존재한다.
이를 막으려면, name[4] = ‘\0’;이라는 인디케이터를 삽입해주어야 한다. 이 인디케이터를 end of string이라고 한다.
 
또는 밑의 코드처럼 입력 해주는 방법도 있다.
 
 
마지막으로 밑의 코드처럼 입력해 주는 방법도 있다. 이 때는 첫 번째 경우에서와 마찬가지로 end of string을 직접 작성해 주어야 한다.
음.. 근데 컴퓨터 문제로 밑 코드가 실행되는지 확인할 수가 없다. 되도록이면 밑의 형태는 사용하지 말자.
 
 
이 때 {}로 묶는 것과 “가 아닌 ‘를 사용해야 하는 것을 명심하자.
이 외의 문자열을 작성할 때는 “를 사용해야 한다.
뭐.. 이건 내 컴퓨터 만의 오류일 수도 있다.
 

- char[] (String: not bulit-in type) : APIs for String Operation

 
string operation을 지원해주는 다양한 연산자들이 있다. 다음 밑의 그림과 같다.
 
notion image
 
밑의 코드처럼 활용할 수 있다.
 
 
  1. strlen 함수는 순수 문자열의 길이를 출력해준다. hello에서 end of string을 빼면 5이므로 5가 출력된다.
  1. sizeof 함수는 전체 size를 알려준다. 초기 20 bytes가 할당 되었으므로 20이 출력된다.’
만약 world가 담겨있는 str2 변수에 strlen과 sizeof 함수를 적용시키면 어떻게 될까? : 이 때는 strlen : 5, sizeof : 6이 출력될 것이다. sizeof 함수는 end of string까지 함께 출력시켜 준다.
  1. strncat_s 함수는 두 문자열을 합쳐준다. hello와 world를 합치는데 world에서 4개의 문자만 합치라고 했으므로 helloworl이 출력된다.
  1. strcmp 함수는 문자열을 사전식으로 비교한다. 예를 들어 abc, abd이면 abc가 더 작다고 판단한다. 같으면, 0 str1이 크면 양의 정수, str2가 크면 음의 정수를 출력한다. 두 문자열이 다르므로 fail을 출력한다.
  1. atoi는 string을 int로 바꿔준다. 200이 출력된다.
 

- 다차원 배열

 
C++에서도 다차원 배열을 정의할 수 있다. 정의는 밑의 코드와 같이 할 수 있다. 중괄호를 사용해야 하는 점 잊지말자.
 
 
주의할 점은 두 번째 index 값은 꼭 정의 되어야 한다는 것이다. 위 코드를 보면 마지막 줄의 첫 번째 index는 정의하지 않았지만 두 번째 index는 정의해준 것을 확인할 수 있다.
 
다음은 예제 코드이다.
 
 
마지막 출력에서는 24와 12가 각각 출력된다. 빈 공간은 0으로 initialization 된다. char에서는 값이 주어지지 않으면 아무것도 들어가지 않지만 int에서는 0으로 초기화 된다는 차이점이 있다.
 

Pointer and Array

 
배열의 이름은 Pointer로 사용될 수 있다.
배열의 0번째 주소와 같은 의미를 가진다.
 
 
위의 코드에서 pNums1는 pNums2와 같은 값을 가진다.
특히 마지막 줄 출력을 보면 배열 이름 = 배열의 첫 번째 element 주소로도 해석 할 수 있지만 역으로 첫번째 element 주소 = 배열 이름으로 해석할 수도 있음을 확인할 수 있다.
 

Pointer Arithmetic

 
Pointer + Integer를 하면 어떤 결과가 나올까? 이는 “다음 칸으로 넘어가라”라는 의미로 해석할 수 있다. 따라서 다음 칸의 주소가 출력된다.
 
 
다차원에서도 활용할 수 있다. 만약 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가 필요할 것이다. 그러나 교수님이 알려주시지 않아서 나도 잘 모른다.
 
 
밑의 그림과 함께 보면 도움이 된다.
 
notion image
 
위 코드는 꼭 꼼꼼히 보고 넘어가도록 하자.
 

5. Array and Pointer (2)

lecture 6- Array and Pointer (2) - release.pdf
330.2KB
 

Array and Pointer review

 
Array and Pointer 복습 부분이다.
 
밑의 코드에서는 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를 우리 마음대로 조정할 수 있다.
 
notion image
 
위 구조에 대해 한 번 더 설명하자면
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

 
notion image
 
먼저 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

 
문법은 밑의 그림과 같다.
 
notion image
 
위를 바탕으로 작성한 코드이다.
 
 
위 코드에서 intializaion을 할 때 첫 줄에서 오류가 생기는 것을 확인할 수 있다. reference type은 이미 있는 값에 대한 별명이기 때문에 initialization을 하지 않은 새로운 변수에 refercence type을 설정하려고 하면 오류가 생기는 것이다. (교수님은 이에 대해서 refererence type에는 null이라는 개념이 없는 것이라고 설명하셨다)
 
즉 initialization 한 변수에 한해서 reference type이 지정 가능하다.
 
int& rNum = iNum; 이 줄은 “iNum 변수에 대하여 rNum이라는 별명 지정해 준다” 정도로 이해해 주면 된다.
 
밑의 코드는 위의 코드에 좀 더 추가해서 적은 것이다. 읽어보고 이해해보자.
 
 

- Difference of reference and pointer

 
Pointer와 Reference는 주소에 접근한다는 점에서 비슷하다고 할 수 있다. 하지만 몇 가지 차이가 존재한다.
 
  1. 변수 주소와 변수 주소의 주소
포인터는 변수 주소와 변수 주소의 주소가 다르다. 하지만 레퍼런스는 동일하다.
 
 
  1. 선언과 초기화
포인터는 선언 이후에 초기화를 해주어도 되지만 레퍼런스는 무조건 선언과 동시에 초기화를 해주어야 한다.
 
  1. 참조 변수에 접근하는 방법
포인터는 * 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이 커지는 방향으로의 이동이기 때문에 결과에 영향을 미치지 않는다.
 
 
 

6. Class (1)

lecture 7 - Classes (1) - release-v2.pdf
356.7KB
 

Why use class?

 
array에 들어갈 수 있는 변수 type은 하나 뿐이다.
 
그러면 여러 type의 변수를 저장하고 싶으면 어떻게 해야할까?
이를 위해 사용하는 것이 Structure와 Class이다.
 

Struct

 
Structure의 선언은 다음과 같이 한다.
 
notion image
 
그림을 보면 structure type 안에 다양한 type의 변수들이 저장되어 있는 걸 확인할 수 있다.
 
실제 사용은 밑과 같이 한다.
 
notion image
 
우리가 변수를 선언할 때는 ‘int a;’ 형태로 선언한다. structure도 마찬가지이다. ‘(struct + struct 이름) + (우리가 생성할 변수의 이름);’ 과 같은 형태로 선언할 수 있다.
 
(structure type은 우리가 직접 만드는 type이라고 이해하면 좋을 것 같다)
 
또는 미리 struct 이름을 정의해 놓고 ‘struct 이름 + 우리가 생성할 변수의 이름’ 형태로 정의할 수도 있다. 이는 위 그림의 오른쪽을 참고하자. 주로 사용하는 형태는 왼쪽이다.
 
예시 코드를 살펴보자.
 
 
주석을 읽어보면서 차근차근 이해해 보아야 한다.
 
여기서 중요히 보아야할 것은 ‘→’와 ‘.’이다. struct 내의 일반 변수에 접근할 때는 .을 이용하고 pointer 변수에 접근할 때는 →을 사용한다.
 
(참고 : cout 부분에서는 dereferencing이 안되는 것 같다. cout << *student2 << endl;를 하면 오류가 생긴다)
 

Class

 

- Introduction to OOP

 
객체지향 프로그래밍은 하나의 프로그래밍 패러다임이다.
 
  • Abstraction
  • Encapsulation
  • Inheritance
  • Polymorphism
 
객체지향 프로그래밍은 이 네 가지 컨셉을 기본으로 하고 있다. (추상화, 캡슐화, 상속, 다형성)
 

- Syntax

 
class도 structure와 마찬가지로 user-defined type이다.
 
class의 문법은 다음과 같다.
 
notion image
 
사용은 다음과 같이 할 수 있다.
 
notion image
 
그런데 structure와 다른 점이 한 가지 있다. class에는 public이라는 키워드가 추가 된 것을 확인할 수 있다. 만약 이 키워드를 없애면 어떻게 될까?
 
 
위 코드를 실행하면 error가 생긴다.
 
notion image
 
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한 프로그래밍이 아니라고 하셨다)
 
또 다른 예제 코드를 살펴보자.
 
 
오류가 생기는 걸 확인할 수 있다. dynamic allocation을 하면서 private accessibility를 가진 id와 name에 접근하려 했기 때문이다.
 
 
다음과 같이 수정하면 error가 생기지 않는다.
하지만 넣어준 값이 아무것도 없기 때문에 출력 값은 없다.
 
만약에 private를 public로 바꾸면 어떤 결과가 출력될까?
 
 
위와 같은 결과가 나오게 된다.
여기서 중요하게 보아야 할 부분은
cout << (*student1).GetID() << endl; cout << student1->GetID() << endl;
여기이다.
Pointer에서 접근할 때는 ‘→’ 기호를 써준 것을 확인할 수 있다. 우리는 여기서 ‘→’가 dereferencing의 의미를 가지고 있다고 해석할 수 있을 것이다 (나만의 해석). 이건 많이 헷갈린다..
 

Constructor / Destructor

 

- Constructor

 
constructor는 class 내부의 member function으로 instance화 될 때 자연스럽게 불려지는 함수이다. 파이썬 class의 init 함수와 같다고 생각하면 될 것 같다.
 
return value는 없고 여러 개의 constructor를 정의할 수 있다.
 
class의 이름과 같은 이름을 가진 함수를 정의해주면 자동으로 constuctor가 된다.
 

- Syntax

 
notion image
 
위와 같이 class 내부에서 constructor를 선언하고 밖에서 constructor를 정의하므로써 사용할 수 있다. class 내부의 constuctor는 heap에 allocation 되고 constructor의 정의는 stack에 allocation 된다.
 

- Destructor

 
말 그대로 constructor를 지워주는 것이다. destuctor도 constructor과 마찬가지로 내부에서 선언되고 외부에서 정의된다.
 
destructor은 한 가지 밖에 없고, default destructor는 compiler에 의해서 정의된다.
 
(++ constructor과 destructor 모두 내부에서 한 번에 선언과 정의를 해도 문제 되지는 않는다)
 

- Example

 
예제 코드를 살펴보자. (음 오류가 많다.. 참고만 하자..)
 
 
밑의 코드도 주의깊게 살펴보도록 하자. 전년도 기출 문제이다.
 
 
 

Member Variable Initialization

 
member variable를 초기화 하는 방법이다.
 
 
 

This Pointer

 
자기 자신을 가르키는 상수
 
 
 
 

7. Class (2)

lecture 8 - Classes (2) - release.pdf
478.7KB
 

UML Diagram

 

- UML diagram

 
UML diagram은 class를 디자인 하기 위한 하나의 graphical design diagram이다.
 
  • classes
  • attributes
  • operations
  • the relationship among objects
를 표현한다.
 
notion image
 
위 사진 처럼 맨 위 구역에는 class, 그 밑 구역에는 attribute, 맨 아래 구역에는 operation을 넣어 표현한다. 추가로 the relationship among objects는 화살표를 이용해 표현한다.
 
실제 코드를 바탕으로 다이어그램을 그려보자.
 
notion image
 
위 그림을 보면 private members는 (-), public members는 (+), parameter type은 괄호 안에, return type은 : 뒤에 적는 것을 확인할 수 있다.
 
위에서 the relationship among objects는 화살표로써 표현한다고 언급했다. 관계에 따라서 서로 다른 화살표 모양을 쓰는데 이는 다음 그림과 같다.
 
notion image
(여기서 association을 has a relationship이라고 부르기도 한다. 교수님께서 말씀해주셔서 추가로 적어둔다)
 
 
 

Practice

 
01. UML Diagram 내용을 바탕으로 연습을 해보자.
 
우리는 밑의 다이어그램을 바탕으로 코드를 짜볼 것이다.
 
notion image
 
Lecture.cpp
 
Lecture.h
 
Student.cpp
 
Student.h
 
Teacher.cpp
 
Teacher.h
 
main
 
main 함수에서 파일을 실행시켜 보면 원하는 결과가 나올 것이다.
 
질문 : 헤더 파일과 cpp 파일로 구분하는 이유는 무엇일까?
헤더 파일은 ‘정의’, c++은 ‘선언’을 한다. 그런데 왜 굳이 구분을 하는 것일까?
 
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; }
class Complex { int* m_r; // real part int* m_i; // imaginary part public: Complex operator+(Complex c2); Complex operator+(Complex& c2); Complex operator+(const Complex& c2); }; 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)); }
class Complex { int* m_r; // real part int* m_i; // imaginary part public: // Constructors (omitted) Complex operator+(double r); friend Complex operator+(double r, const Complex& c); }; 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; }
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; }
class Complex { int* m_r; // real part int* m_i; // imaginary part public: // Constructors (omitted) // + operator overloading (omitted) Complex& operator++(); // prefix Complex operator++(int dummy); // postfix }; Complex& Complex::operator++() { (*m_r)++; return *this; } Complex Complex::operator ++(int dummy) { Complex ret(*this); (*m_r)++; return ret; }
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 { private: protected: string m_name; int* m_Id; 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; } }; class Teacher : protected Person { private: //Nothing public: int GetTeacherID() { return this->GetID(); } }; int main() { Student s1; Teacher t1; s1.SetName("Tom"); t1.SetName("Jenny"); //Error cout << s1.GetStudentName() << endl; cout << t1.GetTeacherID() << endl; return 0; }
class Base { }; class Derived : public Base { }; Derived d; Derived * pD = &d; Base * pB = &d;
int main() { Student s1; Teacher t1; s1.SetName("Tom"); s1.SetID(5555); t1.SetName("Jenny"); t1.SetID(6666); Person* p_arr[] = { &s1, &t1 }; // upcasting 부분 for (int i = 0; i < 2; i++) { cout << p_arr[i]->GetID() << "\t" << p_arr[i]->GetName() << endl; } //cout << p_arr[0]->GetStudentName() << endl; //Error //cout << p_arr[1]->GetTeacherID() << endl; //Error return 0; }
class Base { }; class Derived : public Base { }; Derived d; Base * pB = &d; Derived * pD = (Derived *) pB;
int main() { Student s1; Teacher t1; s1.SetName("Tom"); s1.SetID(5555); t1.SetName("Jenny"); t1.SetID(6666); Person* p_arr[] = { &s1, &t1 }; //p_arr[0] is Student, p_arr[1] is Teacher cout << ((Student*)p_arr[0])->GetStudentName() << endl; cout << ((Teacher*)p_arr[0])->GetTeacherID() << endl; cout << ((Teacher*)p_arr[1])->GetTeacherID() << endl; cout << ((Student*)p_arr[1])->GetStudentName() << endl; return 0; }
class Base { public: Base(){ cout << “base constructor\n”; } }; class Derived : public Base { public: Derived() { cout << “derived constructor\n”; } }; Derived d; // constructor
class Base { public: Base(int a){ cout << “base constructor\n”; } }; class Derived : public Base { public: Derived() : Base(10) { cout << “derived constructor\n”; } }; Derived d; // constructor
class Person { private: protected: string m_name; int* m_Id; 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; } 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; } Student() : Person(10) { cout << "Student constructor called" << endl; }; }; class Teacher : public Person { private: //Nothing public: int GetTeacherID() { return this->GetID(); } Teacher() { cout << "Teacher constructor called" << endl; }; }; int main() { Student s1; Teacher t1; return 0; } ---- Person constructor 2 called Student constructor called Person constructor 1 called Teacher constructor called
int main() { Teacher* t1 = new Teacher(); delete t1; Person* p_person = new Student(); // upcasting delete p_person; return 0; }
class Base { public: Base(){ cout << "1. base constructor" << endl; } ~Base(){ cout << "2. base destructor" << endl; } }; class Derived : public Base { public: Derived() { cout << "3. derived constructor" << endl; } ~Derived() { cout << "4. derived destructor" << endl; } }; int main() { Derived *pD = new Derived; cout << "5. instance created\n"; delete pD; return 0; } ---- 1. base constructor 3. derived constructor 5. instance created 4. derived destructor 2. base destructor
class Person { protected: int* m_Id; string m_name; public: Person() { m_Id = new int(1111); m_name = "None";}; Person(int id) { m_Id = new int(id); m_name = "None";} string GetName() { return m_name; }; int GetID() { return *m_Id; } void SetName(string name) { m_name = name; } void SetID(int id) { *m_Id = id; } void work() { cout << "Person Work" << endl; } string GetClassName() { return "Person"; } }; class Student : public Person { public: Student() : Person(10) {}; string GetStudentName() { return this->m_name; } void work() { cout << "Student studies" << endl; } string GetClassName() { return "Student"; } }; class Teacher : public Person { public: Teacher() {}; int GetTeacherID() { return this->GetID(); } void SetTeacherName(string new_name) { this->SetName(new_name); } string GetTeacherName() { return this->GetName(); } void work(){ cout << "Teacher teaches" << endl; } string GetClassName() { return "Teacher"; } }; int main() { Student s1; Teacher t1; s1.work(); t1.work(); cout << s1.GetClassName() << endl; cout << t1.GetClassName() << endl; return 0; } ---- Student studies Teacher teaches Student Teacher
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. "; } }
//===== Ex1. ===== void func01() { throw “func01"; } int main(){ try{ func01(); } catch (const char* ex) { cout << "Exception at " << ex << endl; } } //===== Ex2. ===== void func03() { throw “func03"; } void func02() { func03(); } void func01() { func02(); } int main(){ try{ func01(); } catch (const char* ex) { cout << "Exception at " << ex << endl; } }
class MyException { int errNo; string errFunc, errMsg; public: MyException(int n, string f, string m): errNo{ n }, errFunc{ f }, errMsg{ m }{} virtual ~MyException() {} void what(){ cout << "Error[" << errNo << "] : " << errMsg << " at " << errFunc << endl; } }; class MyDivideByZero : public MyException{ public: MyDivideByZero(string f) : MyException(100, f, "Divide by Zero") {} }; int main() { int n1{ 10 }, n2{ 0 }; cin >> n2; try { if (n2 == 0) { throw MyException(100, "main()", "Zero"); //throw MyDivideByZero("main()"); //TEST } } catch (MyException& e) { e.what(); } }
#include<exception> int main() { int nSize; char* arr; cout << "Enter Array Size: "; cin >> nSize; try { arr = new char[nSize]; cout << "Array (" << _msize(arr) << ") is created."; delete[] arr; } catch (bad_alloc & e) { cout << e.what() << endl; } }
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 ; }
template<typename T1, typename T2> class Student { T1 id; T2 name; public: Student(T1 id, T2 name) : id(id), name(name) {} void Print() { cout << "id: " << id << " name: " << name; } }; int main() { Student<int, const char*> st1{ 201911000, "Alice" }; Student<const char*, const char*> st2{"A001", "Carol"}; st1.Print(); st2.Print(); }
template<typename T1, typename T2 = const char*> class Student { T1 id; T2 name; public: Student(T1 id, T2 name) : id(id), name(name) {} void Print() { cout << "id: " << id << " name: " << name; } }; int main() { Student<int> st1{ 201911000, "Alice" }; Student<const char*> st2{"A001", "Carol"}; st1.Print(); st2.Print(); }
#include <iostream> template<typename T, int dim> class PointND { T* coordinates; public: PointND() { coordinates = new T[dim]; } ~PointND() { delete[] coordinates; } }; int main() { PointND<double, 2> pt_2d; PointND<double, 3> pt_3d; }
#include <iostream> #include <cstring> #include <string> template<typename T> T Add(T n1, T n2) { return n1 + n2; } template<> const char* Add<const char*>(const char* s1, const char* s2) { string str= s1; str += " "; str += s2; char* cstr = new char[str.length() + 1]; memcpy(cstr, str.c_str(),str.length()+1); return cstr; } int main() { cout << Add<int>(10, 20) << std::endl; cout << Add<double>(1.5, 2.5) << std::endl; cout << Add<const char *>("Hello", "World") << endl; }
#include <iostream> #include <vector> using namespace std; int main() { vector<int> v1; v1.push_back(1); v1.push_back(2); cout << "At 0: " << v1.at(0) << "\tAt 1: " << v1.at(1) << endl; cout << "Front" << v1.front() << "\tBack" << v1.back() << endl; cout << "V1 capacity " << v1.capacity() << "\tV1 Size : " << v1.size() << endl; vector<int> v2(v1); cout << "V2 capacity " << v2.capacity() << "\tV2 Size: " << v2.size() << endl; v1[0] = 3; v1.pop_back(); cout << "first loop" << endl; for (int& a : v1) { cout << a << " "; } v1.push_back(4); cout << "\nsecond loop" << endl; for (int& a : v1) { cout << a << " "; } v1.clear(); cout << "\nthird loop" << endl; for (int& a : v1) { cout << a << " "; }} ---- first loop 3 second loop 3 4 third loop
#include <iostream> #include <vector> using namespace std; int main(){ vector<int> v{ 1,2,3,4 }; vector<int>::iterator iter; for (iter = v.begin(); iter != v.end(); ++iter) // 전위 연산자를 쓰고 있다. { cout << *iter << " "; *iter -= 1; } vector<int>::const_iterator citer{ iter }; // cout << *iter; for (citer = v.begin(); citer != v.end(); ++citer) // 만약에 end일 때 까지로 설정한다면 run-time error가 생기게 된다 { cout << *citer << " "; //*citer -= 1; } }
if (*iter == 3) iter = v.erase(iter) ---- iter = v.begin() v.insert(iter,s)
#include <iostream> #include <vector> using namespace std; int main() { vector<int> v{ 1,2,3,4 }; // for (int i=0; i < v.size(); ++i) { v[i] … } for (vector<int>::iterator iter = v.begin(); iter != v.end(); ++iter) { cout << *iter << " "; } for (auto iter = v.begin(); iter != v.end(); ++iter) { cout << *iter << " "; } for (auto& e : v) cout << e << " "; }
#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 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
(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 (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;
double calArea ( int radius ) { double dVal; dVal = radius * radius * 3.14; return dVal; }
double calArea ( int radius ) { double dVal; dVal = radius * radius * 3.14; return dVal; }
double calArea ( int radius );
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
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
#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
int studentID = 201911999; cout << “address of studentID :” << &studentID; cout << “value at ” << & studentID << “ : “ << *(& studentID); ---- address of studentID :0000009AC9DDFBE4value at0000009AC9DDFBE4: 201911999
data_type * variable_name;
char* name{ 0 }; // nullptr (C++11) int iNum1 = 10; int* pNum1 = &iNum1; // 여기에 & 안 붙이면 당연히 오류 생긴다. 그럼 주소 값이 아니게 되므로
cout << "value:" << iNum1; cout << "its address : " << pNum1; *pNum1 = 20; cout << “Pointer Value: " << *pNum1; cout << “Pointer Value: " << iNum1;
cout<<sizeof(int*)<<endl;
#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의 주소 값
#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; }
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, }; // 중괄호가 아니면 에러 생긴다. 주의하도록 하자.
// “Hello World”, “John” char name[10]; name[0] = 'J'; name[1] = 'o'; name[2] = 'h'; name[3] = 'n'; cout << name << endl; //John****
char name [] = “John”; // char name [4]를 출력 해보면 end of string이 나온다.
char name2 [] = {'J','o','h','n'}; cout << name2 << endl; //end of string 없기 때문에 에러 생김 cout << sizeof(name2) << endl;
#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
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} };
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;
#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
#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
#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
#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
int x = 10; int* ptr = &x; delete ptr; return 0;
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에 변수 넣기 위해 동적할당
#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; 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
ptr1 == &num &ptr1 != &num ---- ref == num &ref == &num
#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
#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; } ----
#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
#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; }
//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(); }
#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 #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; }
#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 #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; }
#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); };
#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; }