<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>rien</title>
    <link>https://ri3n.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 14 Apr 2026 16:33:39 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>_rien</managingEditor>
    <item>
      <title>[CKA] Section 2: Core Concepts</title>
      <link>https://ri3n.tistory.com/108</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;본 글은 Udemy 강의 중 Certified Kubernetes Administrator (CKA) with Practice Tests &lt;br /&gt;내용을 임의로 정리한 내용입니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEYrRC/btsJvVmIQb0/ixw6L7rwWRNGJ1mIZvzcE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEYrRC/btsJvVmIQb0/ixw6L7rwWRNGJ1mIZvzcE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEYrRC/btsJvVmIQb0/ixw6L7rwWRNGJ1mIZvzcE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEYrRC%2FbtsJvVmIQb0%2Fixw6L7rwWRNGJ1mIZvzcE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;225&quot; height=&quot;225&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;12. Docker-vs-ContainerD&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;태초엔 Docker가 있었다. Docker가 융성하면서 이를 효율적으로 관리하기 위해 k8s가 태어났다.&lt;/li&gt;
&lt;li&gt;k8s 커뮤니티가 점점 커지기 시작하자, Rocket과 같은 다른 컨테이너 런타임도 k8s에 들어오고 싶어했다.&lt;/li&gt;
&lt;li&gt;k8s는 그 생명력을 위해서라도 확장성을 가져야 했다. 그래서 다른 컨테이너 런타임도 원한다면 생태계에 들어올 수 있도록 인터페이스를 제공했다. 그게 **Container Runtime Interface** (CRI)다.&lt;/li&gt;
&lt;li&gt;CRI를 구현한 컨테이너 런타임은 Open Container Initiative (OCI) 프로토콜만 지키면 k8s에 편입될 수 있었다. OCI는 크게 imagespec과 runtimespec으로 나뉜다.&lt;/li&gt;
&lt;li&gt;근데 문제는 Docker부터가 CRI를 따르지 않는다는 점이다. 시간 순서상 당연하다. Docker가 먼저 나왔고 표준이라고 할 수 있는 CRI는 그 이후에나 나왔기 때문이다.&lt;/li&gt;
&lt;li&gt;그렇다고 k8s가 Docker를 무시할 수도 없는 노릇이었다. 가장 큰 고객이기 때문. k8s가 Docker를 지원하기 위해 내놓은 것이 walkaround 방식인 dockershim이다.&lt;/li&gt;
&lt;li&gt;Docker는 실제로 여러 컴포넌트의 집합이다. API, Volume, Auth 등&amp;hellip; 그 컴포넌트 중 하나가 containerD다. containerD는 컨테이너를 실행하고 이미지를 관리한다.&lt;/li&gt;
&lt;li&gt;2016년에 Docker는 containerD를 독립 프로젝트로 분리했고 containerD는 주요 기술 기업들이 뛰어들어 발전시켰다. 그결과 containerD는 컨테이너 기술에서 사실상 표준이 되었다. containerD는 OCI 표준을 지킨다.&lt;/li&gt;
&lt;li&gt;Docker는 내부적으로 containerD를 사용하지만 containerD는 스탠드얼론이다. 그러므로 containerD만으로 컨테이너를 띄울 수도 있다! 어떻게? containerD의 CLI가 ctr이다. 매우 제한적인 CLI 툴이긴 하다.&lt;/li&gt;
&lt;li&gt;ctr을 Docker CLI와 비슷한 느낌으로 쓰기 위해서 nerdctl을 쓸 수 있다.&lt;/li&gt;
&lt;li&gt;crictl은 위에서 말한 CRI에 접근하는 CLI툴이다. 이걸 통해서 어느 컨테이너 기술이든 CRI에 맞게 inspect와 debug를 할 수 있다. 쿠버네티스가 컨테이너 런타임을 보는 인터페이스라고 생각하면 된다. (k8s 입장에선 컨테이너 런타임 뭐든지 상관없이 일관된 방식으로 디버깅할 수 있어야 한다)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;13. ETCD for Beginners&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분산되어 있는 - 안전한 - 키밸류 스토어&lt;/li&gt;
&lt;li&gt;./etcd 로 etcd 서버를 시작할 수 있다. etcd 서버는 2379 포트를 열어둔다.&lt;/li&gt;
&lt;li&gt;아래처럼 etcdctl cli client로 새로운 값을 추가할 수 있다. ./etcdctl set key1 value1 ./etcdctl get key1&lt;/li&gt;
&lt;li&gt;2015년 2월에 v2.0이 출시되었고 다음 버전인 v3.1은 2017년 1월에 출시되었다. 이 사이에 큰 변화가 있었다. etcd의 API 버전이 2에서 3으로 바뀌었다. (위에 커맨드는 2버전이다.)&lt;/li&gt;
&lt;li&gt;3버전은 데이터 추가가 바뀌었을 정도로 많이 바뀌었다. ./etcdctl put key1 value1 ./etcdctl get key1&lt;/li&gt;
&lt;li&gt;API 버전을 바꾸고 싶으면 환경변수를 바꾸면 된다. export ETCDCTL_API=3&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;14. ETCD in k8s&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;k8s에 필요한 모든 정보가 ETCD에 저장되고 수정된다. Nodes, PODs, Configs, Secrets, Accounts 등&lt;/li&gt;
&lt;li&gt;k8s 클러스터 배포에는 크게 2가지 방법이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Manual
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;따로 ETCD를 설치하고 실행해야 한다.&lt;/li&gt;
&lt;li&gt;etcd.service에 설정하는 설치 옵션 중에서 --advertise-client-urls가 중요한데, 이게 ETCD 서버의 엔드포인트이기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;kubeadm이 알아서 ETCD를 POD 방식으로 띄워준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HA 환경에서는 ETCD도 여러개 인스턴스가 떠있을 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;16. Kube-API Server&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubectl 커맨드를 입력하면 이 커맨드는 가장 먼저 Kube-API Server에 도달한다. Kube-API Server는 아래 동작을 수행한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청의 권한과 유효성을 검사한다.&lt;/li&gt;
&lt;li&gt;필요한 작업을 수행(Node 생성 등)&lt;/li&gt;
&lt;li&gt;필요하다면 ETCD 서버에 데이터를 업데이트한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Kube-API는 k8s의 모든 컴포넌트 간 통신을 책임진다. 이 때, TLS를 통해 보안을 적용한다. (모든 컴포넌트 간 통신에 TLS가 필요하다)&lt;/li&gt;
&lt;li&gt;Kube-API 서버의 설정은 아래 경로에서 확인할 수 있다. &lt;br /&gt;cat /etc/systemd/system/kube-apiserver.service&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;17. Kube Controller Manager&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kube Controller Manager는 각기 다른 역할을 가진 오피스의 연합이라고 이해하면 된다. (또는 클러스터의 뇌)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Node-Controller는 Node의 상태를 주시하며 그에 따른 대응을 수행한다. 당연히 이 대응도 Kube-API Server를 통한다.&lt;/li&gt;
&lt;li&gt;이렇게 많은 것들이 Kube Controller Manager라는 하나의 프로세스로 떠서 수행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;18. Kube Scheduler&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Scheduler는 &lt;b&gt;어떤 POD가 어느 Node에 가야할 지 결정하는 것만 수행&lt;/b&gt;한다. 실제로 POD를 Node에 올리지는 않는다.&lt;/li&gt;
&lt;li&gt;실제로 올리는 건 Kubelet의 역할이다.&lt;/li&gt;
&lt;li&gt;왜 스케쥴러가 필요할까?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수많은 배(=Node)가 있다고 했을 때, 컨테이너(=Pod)를 어떤 배에 실을 지 결정하는 건 생각보다 쉬운 일이 아니다. 컨테이너를 실을 수 있을 정도로 배에 Capacity가 있는 지도 알아야 하고 성능도 알아야 한다.&lt;/li&gt;
&lt;li&gt;또한 각 컨테이너가 어디에 도착(Endpoint)해야 하는지도 알아야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;19. Kubelet&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kubelet은 배(=워커 노드)의 선장이다. 배 안에서 일어나는 모든 일을 관장한다. 마스터 쉽(=컨트롤러 노드)와도 교류하는 서류 작업도 해야 한다.&lt;/li&gt;
&lt;li&gt;Scheduler가 배에 컨테이너를 실어야 겠다고 결정 &amp;rarr; Scheduler가 API Server를 통해 Kubelet에 요청 &amp;rarr; Kubelet은 컨테이너 런타임을 통해 Pod를 생성&lt;/li&gt;
&lt;li&gt;Kubeadm은 다른 컴포넌트와 달리 자동으로 Kubelet을 자동으로 설치하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;20. Kube-proxy&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드에 배포된 pod는 다른 pod와 통신할 수 있다. 이걸 가능하게 해주는 게 Kube Proxy다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;21. Recap - Pods&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래 환경이 다 갖춰졌다고 가정한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션은 개발이 완료되어 이미지로 빌드되었다.&lt;/li&gt;
&lt;li&gt;k8s 클러스터가 배포되어 실행 중이다.&lt;/li&gt;
&lt;li&gt;싱글 노드 셋업이든 멀티 노드 셋업이든 상관없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;k8s는 절대 컨테이너를 워커 노드에 직접 배포하지 않는다. POD로 캡슐화해서 배포한다. Pod는 &lt;b&gt;애플리케이션 인스턴스 한 개&lt;/b&gt;다.&lt;/li&gt;
&lt;li&gt;Pod는 k8s로 만들 수 있는 가장 작은 단위다. 즉, 트래픽이 많아져서 더 많은 앱이 필요하다면 POD에 두 개 앱을 띄울 수는 없고 워커노드에 Pod를 더 배포해야 한다.&lt;/li&gt;
&lt;li&gt;만약 워커 노드에 Capacity가 부족하다면 워커 노드를 더 배포해야 한다.&lt;/li&gt;
&lt;li&gt;그렇다면 POD는 무조건 한 개 컨테이너만 배포할 수 있을까? 그렇지 않다. 보통 같은 컨테이너 여러개를 한 POD에 배포하진 않을 뿐이다. 보통 App과 Helper 컨테이너를 함께 배포한다.&lt;/li&gt;
&lt;li&gt;이렇게 보면 왜 굳이 POD가 필요할까 싶을 것이다. Node &amp;gt; POD &amp;gt; Container &amp;gt; App의 4중 구조가 너무 과해보이는 것이다. 하지만 이 구조는 Long Run을 염두에 뒀을 때 매우 유리하다. POD가 없이(그나마 뺄 수 있는게 POD니까) 여러개 컨테이너를 띄웠다면 이들 사이에 &lt;b&gt;볼륨이나 네트워크를 전부 수동으로! 설정&lt;/b&gt;해줘야 한다. 만약 Helper Container가 있다면 그것도 수동으로 만들어주고 네트워크 설정도 해줘야 할 것이다. POD는 이것들을 전부 알아서 해준다.&lt;/li&gt;
&lt;li&gt;실제로 POD는 어떻게 배포할까? 아래 명령어를 사용한다. kubectl run nginx --image nginx&lt;/li&gt;
&lt;li&gt;nginx 이미지를 가지고 POD를 배포한다. 아래 명령어로 확인할 수 있다. kubectl get pods&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;22. Pods with YAML&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;POD를 배포하는데 필요한 모든 정보를 YAML에 담아서 저장하고 실행할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
	name: myapp-pod
	label:
		app: myapp
		type: front-end
spec:
	containers:
		- name: nginx-container
			image: nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubectl create -f pod-definition.yml&lt;/li&gt;
&lt;li&gt;kubectl get pods : 전체 POD 목록 조회&lt;/li&gt;
&lt;li&gt;kubectl describe pod myapp-pod : POD 상세 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;29. Recap - ReplicaSets&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Replication Controller&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;POD를 특정 개수로 유지해준다.&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HA를 위해 여러 POD를 띄우도록 도와준다.&lt;/li&gt;
&lt;li&gt;그렇다면 Single POD를 띄울 생각이면 Replication Controller 필요없을까? 아니다. Replication Controller를 통해 POD가 죽으면 자동으로 살리도록 설정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로드밸런싱 &amp;amp; 스케일링&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트래픽이 많아짐에 따라 POD를 늘리고 트래픽을 분산시킬 수 있다. 만약 워커 노드의 리소스가 부족하다면 다른 워커 노드에도 POD를 배포할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;vs Replica Set&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Replication Controller가 더 오래된 개념이고 Replica Set으로 대체되었다.&lt;/li&gt;
&lt;li&gt;Replica Set이 더 선호되고 위 기능도 다 지원한다. 즉, Replica Set이 Replica Controller의 상위호환이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;yaml로 Replication Controller 생성하기&lt;/h3&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: 1 # RC는 1만 지원한다.
kind: ReplicationController
metadata:
	name: myapp-rc
	labels:
		app: myapp
		type: front-end
spec:
	template:
		metadata:
			name: myapp-pod
			labels:
				app: myapp
				type: frontend
		spec:
			containers:
			- name: nginx-container
				image: nginx
	replicas: 3
		

&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Replica Sets&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;yaml&lt;/h3&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: apps/v1
kind: ReplicaSet
metadata:
	name: myapp-replicaset
	labels:
		app: myapp
		type: front-end
spec:
	template:
		metadata:
			name: myapp-pod
			labels:
				app: myapp
				type: frontend
		spec:
			containers:
			- name: nginx-container
				image: nginx
	replicas: 3
	selector: # major difference
		matchLabels:
			type: front-end
	
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Labels and Selectors&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Replica Set은 단순히 POD 배포가 아니라 관리까지 역할한다.&lt;/li&gt;
&lt;li&gt;Replica Set은 어떻게 자기가 봐야할 POD를 선별할까? 백만개 POD 중에서 자기가 관리해야 하는 POD가 뭔지 어떻게 알 수 있는가?&lt;/li&gt;
&lt;li&gt;select가 바로 그 필터다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Scale&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;replicas를 3에서 6으로 늘리고 싶다면 어떻게 할까? 가장 쉬운 방법은 yaml에서 숫자를 고친 후 아래 커맨드를 입력하는 것이다. kubectl replace -f replicaset-definition.yml&lt;/li&gt;
&lt;li&gt;kubectl scale --replicas=6 -f replicaset-definition.yml 도 된다. kubectl scale &amp;mdash;replicas=6 replicaset myapp-replicaset&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;32. Deployments&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ReplicaSet 같은 개념은 모두 잊고 일단 아래 상황에 집중해보자.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;백엔드 서버를 여러개 띄워야 하는 상황이다.&lt;/li&gt;
&lt;li&gt;이때, 새로운 버전이 Docker Registry에 등록되면 띄워진 백엔드 서버도 부드럽게 전환하고 싶다.&lt;/li&gt;
&lt;li&gt;이 전환이 제대로 안됐다면 버전 롤백도 할 수 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이걸 해주는게 Deployment다. 적용하는 방법은 쉽다. 그냥 위에 yaml에서 kind를 Deployment로 바꾸면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;36. Services&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클러스터 외부와 통신할 수 있게 해준다.&lt;/li&gt;
&lt;li&gt;예를 들어, FE POD 그룹을 외부 클라이언트와 연결해주고 BE POD 그룹을 DB와 연결해준다. 또한 다른 POD 그룹 간에도 연결해준다.&lt;/li&gt;
&lt;li&gt;예를 들어, 아래와 같은 상황을 가정해보자.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Client : 192.168.1.10&lt;/li&gt;
&lt;li&gt;Worker Node : 192.168.1.2&lt;/li&gt;
&lt;li&gt;POD Network : 10.244.0.0&lt;/li&gt;
&lt;li&gt;HTTPD POD : 10.244.0.2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Client가 HTTPD POD에 http 요청을 보내려면 어떻게 해야할까?&lt;/li&gt;
&lt;li&gt;가장 직접적인 방법은 SSH로 Worker Node에 접속한 다음, curl &amp;lt;http://10.244.0.2&amp;gt; 를 하는 것이다. 이 방법은 외부에 서비스를 제공할 수 없기 때문에 의미가 없다.&lt;/li&gt;
&lt;li&gt;이 때, 필요한게 &lt;b&gt;Service&lt;/b&gt;다.&lt;/li&gt;
&lt;li&gt;Service는 앞서 배운 ReplicaSet 또는 Deployment와 같이 k8s 오브젝트다. Service 역할 중하나가 Worker Node의 Port를 리슨해서 트래픽을 POD에 포워딩해주는 것이다. 즉, Service를 통해 우리는 SSH 연결없이 바로 아래 요청이 가능하다. curl http://192.168.1.2:30008&lt;/li&gt;
&lt;li&gt;Service Type은 아래와 같다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NodePort : 트래픽을 포워딩해서 POD에 전해준다.&lt;/li&gt;
&lt;li&gt;ClusterIP : Virtual IP를 생성해 Cluster 내부 통신을 지원한다.&lt;/li&gt;
&lt;li&gt;LoadBalancer : 지원하는 클라우드 프로바이더라면 로드밸런싱&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;yaml
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 selector에 해당하는 pod가 많으면 기본적으로 랜덤으로 할당한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;apiVersion: v1 kind: Service metadata: name: myapp-service spec: type: NodePort ports: - targetPort: 80 # 대상 POD가 공개한 port port: 80 # 서비스의 port nodePort: 30008 # 워커 노드에 연결할 port selector: app: myapp type: front-end&lt;/li&gt;
&lt;li&gt;Service는 노드 안에 있는건데, 대상이 여러 노드안에 있으면 어떻게 될까? k8s가 알아서 멀티 노드로 서비스를 만들어준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;37. Services - Cluster IP&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FE POD가 여러대 있고 BE POD도 여러대, DB POD도 여러대가 있을 때, 이들 사이의 연결을 어떻게 설정해야 할까?&lt;/li&gt;
&lt;li&gt;각 POD는 유동 IP를 가지고 있을것이다. 이 IP는 유동이니까 사용할 순 없다.&lt;/li&gt;
&lt;li&gt;k8s Service를 사용해서 POD들을 그루핑하고 이 그룹에 접근하기 위한 싱글 인터페이스를 제공할 수 있다.&lt;/li&gt;
&lt;li&gt;yaml&lt;/li&gt;
&lt;li&gt;apiVersion: v1 kind: Service metadata: name: back-end spec: type: ClusterIP ports: - targetPort: 80 port: 80 selector: app: myapp type: back-end&lt;/li&gt;
&lt;li&gt;사용&lt;/li&gt;
&lt;li&gt;kubectl create -f service-definition.yml kubectl get services&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;38. Services - Loadbalancers&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;yaml&lt;/li&gt;
&lt;li&gt;apiVersion: v1 kind: Service metadata: name: back-end spec: type: LoadBalancer ports: - targetPort: 80 port: 80 nodePort: 30008&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;41. Namespaces&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Default 네임스페이스는 k8s가 생성한 네임스페이스다.&lt;/li&gt;
&lt;li&gt;k8s가 시스템 구성을 위해 생성한 자원은 kube-system 네임스페이스를 가진다.&lt;/li&gt;
&lt;li&gt;k8s가 생성한 자원 중 모든 유저가 사용할 수 있는 자원은 kube-public네임스페이스를 달고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Isolation&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네임스페이스의 기본 기능이 Isolation이다.&lt;/li&gt;
&lt;li&gt;개발계와 운영계가 같은 클러스터를 쓰되, 격리를 하고 싶다면 각자 다른 네임스페이스를 사용하면 된다.&lt;/li&gt;
&lt;li&gt;네임스페이스는 멀티 노드이지만 리소스 제한을 걸 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DNS&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 네임스페이스를 가진 POD는 서로를 Name으로 바로 찾아낼 수 있다. mysql.connect(&amp;rdquo;db-service&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;다른 네임스페이스의 POD를 찾아내려면 그 네임스페이스를 전부 써줘야 한다. mysql.connect(&amp;rdquo;db-service.dev.svc.cluster.local&amp;rdquo;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Commands&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;kubectl get pods # Default NS의 모든 POD
kubectl get pods --namespace=kube-system # kube-system NS의 모든 POD
kubectl create -f definition.yaml # Default NS에 POD 배포
kubectl create -f definition.yaml --namespace=dev # dev NS에 POD 배포
# NS를 지정하려면 yaml에 meta &amp;gt; namespace에 작성할 수도 있다.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Create Namespace&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;by YAML file&lt;/h3&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;apiVersion: v1
kind: Namespace
metadata:
	name: dev
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;by Commands&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;kubectl create namespace dev
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Switch current namespace&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;kubectl config set-context $(kubectl config current-context) \\
--namespace=dev

kubectl get pods # dev NS의 POD 검색
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Set NS ResourceQuota&lt;/h2&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;apiVersion: v1
kind: ResourceQuota
metadata:
	name: compute-quota
	namespace: dev
spec:
	hard:
		pods: &quot;10&quot;
		requests.cpu: &quot;4&quot;
		requests.memory: 5Gi
		limits.cpu: &quot;10&quot;
		limits.memory: 10Gi
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Imperative vs Declarative&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Imperative Approach는 택시 기사님께 갈림길에서 어디로 꺾으면 되는지 하나하나 설명하는 것과 같다. = 명령어를 하나하나 다 쓰는 것&lt;/li&gt;
&lt;li&gt;Declarative Approach는 택시 기사님께 도착지 주소를 알려주는 것과 같다. = yaml을 하나 작성해서 실행하는 것 = 결과물을 예측하기 쉽고 git 등으로 리뷰를 받을 수도 있다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding</category>
      <category>CKA</category>
      <category>k8s</category>
      <category>Kubernetes</category>
      <category>udemy</category>
      <author>_rien</author>
      <guid isPermaLink="true">https://ri3n.tistory.com/108</guid>
      <comments>https://ri3n.tistory.com/108#entry108comment</comments>
      <pubDate>Sun, 8 Sep 2024 23:54:34 +0900</pubDate>
    </item>
    <item>
      <title>봄밤</title>
      <link>https://ri3n.tistory.com/107</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGpVbw/btrVTT6ck6i/x1KKjmM4Sg0roBmPctkLak/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGpVbw/btrVTT6ck6i/x1KKjmM4Sg0roBmPctkLak/img.jpg&quot; data-alt=&quot;『봄밤』 - 안녕 주정뱅이 中, 권여선&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGpVbw/btrVTT6ck6i/x1KKjmM4Sg0roBmPctkLak/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGpVbw%2FbtrVTT6ck6i%2Fx1KKjmM4Sg0roBmPctkLak%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;434&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;『봄밤』 - 안녕 주정뱅이 中, 권여선&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;봄밤은 변방의 이야기다. 요양원이라는 물리적으로는 우리와 가깝지만 심리적으로는 브라질보다도 먼 그곳의 이야기. 영경과 수환은 배신당한 이들이다. 수환은 거래처 사장과 아내에게 배신당했다. 영경은 전남편과 전시부모, 그리고 두 언니에게 배신당했다. 영경은 그 후유증으로 술을 마시다 결국 자신에게도 배신당했다. 의사도 거기에 거들었다. 그는 몸의 반응은 알코올이 가져다주는 거짓이라고 말했다. 그녀는 몸의 떨림은 물론, 자신의 감정까지 믿지 못하게 되었다. 모든 것이 환상이라고 치부해야 했다. 아니, 어쩌면 그녀 자신이 모든것을 받아들이지 못한 것일수도 있겠다.&lt;/p&gt;
&lt;p&gt;  &amp;quot;산다는 게 참 끔찍하다. 그렇지 않니?&amp;quot; 작가가 표현해낸 요양원에서의 삶은 끔찍하다. 수많은 갈등과 반목, 욕망에 대한 패배 그리고 가난까지. 그곳은 변방이고 변방이기에 황량하다. 인간적인 어떤것도 주어지지 않는다. 하지만 독자는 영경과 수환을 바라보며 감동을 느낀다. 그것은 어려운 환경에서도 꽃피우는 애정의 애틋함이 아니다. 그것보다는 입체적인 것이다. 끔찍하기에 그곳의 행동들은 알알히 빛난다. 그 파멸의 불꽃은 수환의 죽음과 그에 말미암은 영경의 정신이 산산히 부서짐으로서 끝맺는다. 산다는 것은 끔찍하면서도 아름다운 것. 그래, 우리네 삶은 봄밤과 같다. 봄밤은 분명 어둡지만 또한 아름답다.&lt;/p&gt;</description>
      <category>Essay</category>
      <author>_rien</author>
      <guid isPermaLink="true">https://ri3n.tistory.com/107</guid>
      <comments>https://ri3n.tistory.com/107#entry107comment</comments>
      <pubDate>Tue, 10 Jan 2023 20:36:51 +0900</pubDate>
    </item>
    <item>
      <title>[C++ Primer Plus] 18. Visiting with the New C++ Standard</title>
      <link>https://ri3n.tistory.com/106</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;대답해야 할 질문들&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;auto 타입의 type deduction 시점은 언제인가?&lt;/li&gt;
&lt;li&gt;decltype의 사용법은 어떻게 되는가?&lt;/li&gt;
&lt;li&gt;move semactics는 어떻게 사용할 수 있는가?&lt;/li&gt;
&lt;li&gt;c++11의 클래스에서 자동생성되는 함수 6개는 무엇인가?&lt;/li&gt;
&lt;li&gt;override와 final 키워드의 의미는 무엇인가?&lt;/li&gt;
&lt;li&gt;람다식이 함수 포인터와 펑터보다 우월한 이유는 무엇인가?&lt;/li&gt;
&lt;li&gt;wrapper를 쓰는 이유는 무엇인가?&lt;/li&gt;
&lt;li&gt;variadic template은 어떻게 사용할 수 있는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;C++11 Features Revisited&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;New Types&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++11에서 64bit int 지원을 위해 long long, unsigned long long 타입을 추가됐다. 비슷한 이유로 char16_t, char32_t도 추가됐다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Uniform Initialization&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중괄호 {}를 통해 여러 자료형의 초기화를 동일한 형태로 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657248215287&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int* ar = new int[4] {2, 4, 6, 7};
Stump s2{5, 43.4};
Stump s3 = {4, 32.1};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;initialization-list는 narrowing을 방지해주는 기능도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657263245063&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;char c1 = 1.57e27; // double_to_char == undefined behavior(compile OK)
char c2 = 1234567890; // int_to_char == undefined behavior(compile OK)

char c1 {1.57e27}; // compile error
char c2 = {1234567890}; // compile error&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;initializer-list를 파라미터로 받으려면 std::initializer_list&amp;lt;double&amp;gt;을 쓰면 된다. 이를 이용해서 initializer_list로 클래스 오브젝트를 초기화할 때 어떻게 동작할지 설계할 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657263469302&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double sum(std::initializer_list&amp;lt;double&amp;gt; il)
{
	double tot = 0;
    for (auto p = il.begin(); p != il.end(); ++p)
    	tot += *p;
    return tot;
}

int main()
{
	sum({2.5, 4.14, 3.2});
...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Declarations&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;auto 타입은 complier에게 type deduction을 통해서 적절한 type으로 선정케 한다. 이를 이용해 template 선언을 짧게 가져갈 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657263691188&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (std::initializer_list&amp;lt;double&amp;gt;::iterator p = il.begin();
p != il.end(); p++)
// C++11
for (auto p=il.begin(); p!=il.end(); p++)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;decltype을 활용해서 컴파일 시점에 타입을 추론-선언 해서 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657263823599&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T, typename U&amp;gt;
void ef(T x, U y)
{
	decltype(T*U) tu; // T와 U를 곱했을 때 타입을 컴파일 시점에 추론해서 쓸 수 있게 해준다.
    tu a;
... 
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Return Type에 대한 컴파일 타임 타입 추론은 decltype으로 바로 적용하기 난해한 부분이 있다. 그 경우, auto 키워드와 연계해서 다음과 같이 작성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657263912050&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;class T, class U&amp;gt;
auto eff(T t, U u) -&amp;gt; decltype(T*U)
{ ... }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Template이 들어오면서 특정 type은 그자체로 이름이 매우 길어질 수 있다. 이 경우, alias를 지정해두면 편한데, 이를 위해 using = 구문을 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657263989460&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using itType = std::vector&amp;lt;std::string&amp;gt;::iterator;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;nullptr&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C는 역사적으로 포인터 변수의 초기값을  0으로 설정해서 널포인터로 사용했다.&lt;/li&gt;
&lt;li&gt;그 결과, 포인터 변수도 0으로 초기화하고 정수형 변수도 0으로 초기화하게 되다보니 혼동이 생겼고 &lt;b&gt;'포인터를 널값으로 설정한다.'&lt;/b&gt;라는 명확한 의미를 내가 위해서 C++11에서 nullptr이 생겼다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Smart Pointers&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++98의 auto_ptr이 폐지되고, unique_ptr / shared_ptr / weak_ptr 3개의 스마트 포인터가 들어왔다. 관련 내용은 16장에서 자세히 서술했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Exception Specification Changes&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++98에서 특정 함수가 어떤 Exception을 던지는지 설명해주는 구문은 다음과 같았다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657264333342&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void f501(int) throw(bad_dog); // f501은 bad_dog exception만 던진다.
void f733(long long) throw(); // f733은 exception을 던지지 않는다.&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하지만 이러한 명세가 실제로 잘 쓰이지 않았다. 하지만 throw()의 경우, 어떠한 exception도 던지지 않는다는 보장을 가지기 때문에 이에 대해서만 C++11에서 새로운 키워드 noexcept가 생겼다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657264415441&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void f875(short, short) noexcept;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Scoped Enumerations&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전통적인 C++의 enum은 상수를 선언하는데에 사용됐다. 이에 대해 C++11의 Scoped Enum을 통해 두가지를 보완한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내부적으로 int가 아닌 다른 자료형으로 enum을 저장할 수 있다.&lt;/li&gt;
&lt;li&gt;따로 name scope를 가지기 때문에 이름을 겹칠 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Class Changes&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 설계에 도움을 주는 여러 기능이 추가됐다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;explicit Conversion Operators&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;class에 대한 automatic type conversion이 기술적인 문제를 발생시키곤 한다. 이를 방지하기 위해 explicit 키워드를 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657265177242&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Plebe
{
	Plebe(int); // implicit int-to-Plebe enabled
    explicit Plebe(double); // implicit double-to-Plebe disabled
};

Plebe(5); // OK (explicit)
Plebe(5.0); // OK (explicit)
Plebe a, b;
a = 5; // OK (implicit)
b = 5.0; // ERROR (implicit)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Member In-Class Initialization&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++에서 멤버 변수의 초기값은 무조건 생성자에서 설정해줘야 했다. C++11에선 In-Class Initialization을 통해 기본값을 설정해줄 수 있다. 문법은 다음과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657265326170&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Session
{
	int mem1 = 10; // in-class initialization
    double mem2 {1966.54}; // in-class initialization
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Ranged-based for loop&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 문법을 통해 시퀀스를 순회하는 알고리즘을 좀더 가독성이 좋게 작성할 수 있다. 이는 STL Container에서도 작동된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657265507144&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double prices[5] = {4.99, 10.99, 6.87, 7.99};
for (auto x : prices)
	std::cout &amp;lt;&amp;lt; x &amp;lt;&amp;lt; std::endl;
    
std::vector&amp;lt;int&amp;gt; vi(6);
for (auto&amp;amp; x: vi) // ref로 받아서 값을 조작할 수 있다.
	x = std::rand();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Angle Brackets&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++98에서는 Template에서 쓰는 &amp;lt;&amp;gt;가 연속으로 등장하면 &amp;lt;&amp;lt;나 &amp;gt;&amp;gt; 연산자와의 혼동될 수 있기 때문에 빈칸을 두도록 강제했다. C++11에서는 빈 칸이 없어도 잘 작동된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657265657412&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;std::vector&amp;lt;std::list&amp;lt;int&amp;gt;&amp;gt; vl; // OK in C++11&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;The rvalue Reference&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++98에서 쓰던 레퍼런스는 모두 lvalue reference로 본다. lvalue는 프로그램에서 접근할 수 있는 주소값을 가지고 있는 데이터를 의미한다. 예를 들어, 변수나 배열 요소 등이 이에 속한다. 이에 비해 rvalue는 임시 데이터를 의미한다고 이해하면 쉽다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657265876071&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int x = 10;
int&amp;amp; rx = x; // lvalue ref
int&amp;amp;&amp;amp; rrx = 13; // rvalue ref&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;rvalue Reference는 &amp;amp;&amp;amp;를 통해 만들어낼 수 있고, 이게 생긴 이유는 바로 다음에 설명할 Move Semantics 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Move Semantics and the Rvalue Reference&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;The Need for Move Semantics&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음 상황을 생각해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657266071410&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vector&amp;lt;string&amp;gt; vstr;
// 1000글자짜리 스트링을 20000개 vstr에 저장한다.
...
vector&amp;lt;string&amp;gt; vstr_copy(vstr);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 경우에 vstr_copy는 copy constructor를 사용해서 const ref로 vstr을 받아 복사할 것이다.&lt;/li&gt;
&lt;li&gt;이 때, string vector에 각 요소마다 대문자만 가지고 있는 벡터를 만들어주는 함수를 작성하면 다음과 같을 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657266235571&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vector&amp;lt;string&amp;gt; allcaps(const vector&amp;lt;string&amp;gt;&amp;amp; vs)
{
	vector&amp;lt;string&amp;gt; temp;
    
    (vs에 담긴 대문자만 뽑아서 temp에 저장한다)....
    
    return temp;
}

vector&amp;lt;string&amp;gt; vstr;
.......
vector&amp;lt;string&amp;gt; vstr_copy(vstr);
vector&amp;lt;string&amp;gt; vstr_copy2(allcaps(vstr));&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자 이 때, 마지막 줄에서 값 복사는 몇번이나 일어날까? 컴파일러마다 다르겠지만 최악의 경우, 2번까지 일어날 수 있다. (return될 때 한번, copy constructor에서 한번)&lt;/li&gt;
&lt;li&gt;move semantics는 이러한 낭비를 없애기 위해 등장했다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;실제로 move semantics는 다음과 같은 생성자나 대입 연산자 구현에서 이득을 볼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657266826492&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Useless::Useless(Useless&amp;amp;&amp;amp; f) : n(f.n)
{
	pc = f.pc; // steal address
    f.pc = nullptr;
    f.n = 0;
    ShowObject();
}

Useless&amp;amp; Useless::operator=(Useless&amp;amp;&amp;amp; f)
{
	if (this == &amp;amp;f)
    	return *this;
    delete [] pc;
    n = f.n;
    pc = f.pc;
    f.n = 0;
    f.pc = nullptr;
    return *this;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약, lvalue ref를 가지고 move semantics를 강제하려면 std::move를 쓰면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657267074231&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Chunk one;
...
Chunk two;
two = std::move(one);&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;New Class Features&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;move semantics로 인해 클래스에 몇가지 추가사항이 생겼다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Special Member Functions&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;default로 생성되는 함수로 기존 4개에 더해 2개가 추가됐다. defaulted move constructor와 defaulted move assignment가 추가 됐다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657267242207&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Someclass::Someclass(Someclass &amp;amp;&amp;amp;); // defaulted move constructor
SomeClass&amp;amp; Someclass::operator=(Someclass &amp;amp;&amp;amp;); //defaulted move assignment&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Defaulted and Deleted Methods&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;default function을 쓰거나 쓰지 않겠다고 명시적으로 선언할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657267359222&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Someclass(const Someclass &amp;amp;) = delete;
Someclass(Someclass &amp;amp;&amp;amp;) = default;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Delegating Constructors&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존에 있던 생성자를 사용해서 생성자 구현을 단순화할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657267640411&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Notes::Notes(int kk, double xx, std::string stt)
	: k(kk),
    x(xx),
    st(stt) {/* do stuff */}
Notes::Notes() : Notes(0, 0.01, &quot;Oh&quot;) {/* do stuff */}
Notes::Notes(int kk) : Notes(kk, 0.01, &quot;Ah&quot;) {/* do stuff */}
Notes::Notes(int kk, double xx) : Notes(kk, xx, &quot;Uh&quot;) {/* do stuff */}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Managing Virtual Methods: override and final&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;derived class에서 override를 명시적으로 표기하기 위해 override 키워드를 메소드 끝에 쓸 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657459350783&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;virtual void f(char* ch) const override { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;final 키워드는 override와는 완전히 다른 의미다. virtual 함수가 해당 클래스 이후로는 더이상 상속되지 않음을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657459426396&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;virtual void f(char ch) const final {...}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Lambda Functions&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;lambda 함수는 아래처럼 생겼다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657459597812&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[&amp;amp;count](int x){count += (x % 13 == 0);}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간단하게 생각하면 원래 함수 형태에서 &lt;b&gt;리턴 타입과 함수 이름을 없애고 대괄호[]로&lt;/b&gt; 바꾸면 된다.&lt;/li&gt;
&lt;li&gt;리턴 타입은 타입 추론으로 결정되는데, 만약 타입을 결정하고 싶으면 아래처럼 화살표로 표기하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657461306701&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[&amp;amp;count](int x)-&amp;gt;int{count += (x % 13 == 0);}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;The How of Function Pointers, Functors, and Lambdas&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;STL 알고리즘에는 Function Pointer, Functor, Lambda를 전부 줄 수 있다. 먼저 형태를 하나씩 살펴보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657460069395&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* Function pointer */
bool f3(int x) {return x % 3 == 0;}
bool f13(int x) {return x % 13 == 0;}

int count3 = std::count_if(numbers.begin(), numbers.end(), f3);
int count13 = std::count_if(numbers.begin(), numbers.end(), f13);

/* Functor */
class f_mod
{
private:
	int d;
public:
	f_mod(int _d = 1) : d(_d) {}
    bool operator() (int x) {return x % d == 0;}
}

int count3 = std::count_if(numbers.begin(), numbers.end(), f_mod(3));
int count13 = std::count_if(numbers.begin(), numbers.end(), f_mod(13));

/* Lambda */
int count3 = std::count_if(numbers.begin(), numbers.end(),
			[](int x){return x % 3 == 0;});
int count13 = std::count_if(numbers.begin(), numbers.end(),
			[](int x){return x % 13 == 0;});&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;The Why of Lambdas&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왜 C++은 대체재가 충분히 있는데도 불구하고 Lambda를 추가했을까?&lt;/li&gt;
&lt;li&gt;Lambda의 사용 이유를 근접성(proximity), 간결성(brevity), 효율성(efficiency), 확장 가능성(capability)의 측면에서 알아보자.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;근접성(proximity)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;lambda로 작성하면 실제로 전달되는 알고리즘을 바로 볼 수 있다는 장점이 있다. 이와 달리 함수 포인터나 펑터 모두 따로 정의를 살펴봐야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;간결성(brevity)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;functor는 lambda나 함수 포인터에 비해 코드가 길다는 단점이 있다.&lt;/li&gt;
&lt;li&gt;이 관점에선 함수 포인터가 람다에 비해 우월하다고 생각될 수 있지만 그렇지 않다. auto 타입으로 람다를 담을 수 있기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657462663552&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;auto mod3 = [](int x){return x % 3 == 0;};
count1 = std::count_if(n1.begin(), n1.end(), mod3);
count2 = std::count_if(n2.begin(), n2.end(), mod3);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;효율성(efficienty)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;function pointer은 따로 inline 설정을 안하면 컴파일러가 inline을 보통 설정하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;확장 가능성(capability)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;lambda는 lambda가 사용되는 scope의 로컬 변수를 capture할 수 있어서 작성 시에 자유도가 뛰어나다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657463127806&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int count13 = 0;
std::for_each(numbers.begin(), numbers.end(),
	[&amp;amp;count13](int x){count13 += x % 13 == 0;});
// capture에 &amp;amp;만 쓰면 모든 지역 변수에 접근할 수 있다.
std::for_each(numbers.begin(), numbers.end(),
	[&amp;amp;](int x){count13 += x % 13 == 0;});&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Wrappers&lt;/h2&gt;
&lt;pre id=&quot;code_1657463459960&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;answer = ef(q);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기서 문제, ef는 뭘까?&lt;/li&gt;
&lt;li&gt;ef는 함수일수도, 함수 포인터일 수도, 펑터일수도, 람다 변수일 수도 있다. 이런 타입을 모두 callable types라고 부른다.&lt;/li&gt;
&lt;li&gt;이렇게 callable type이 많으면 template 함수를 사용할 때 비효율을 가져올 수 있다. 아래 예시를 보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657464498537&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double dub(double x) {return 2.0*x;}
double square(double x) {return x*x;}

use_f(y, dub);
use_f(y, square);
use_f(y, Fp(5.0)); // return과 parameter가 모두 double인 functor
use_f(y, Fp2(5.0)); // return과 parameter가 모두 double인 functor
use_f(y, [](double u) {return u*u;});&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 상황에서 template은 총 몇개가 만들어질까?&lt;/li&gt;
&lt;li&gt;정답은 5개다. 그런데 정말 5개나 만들어질 필요가 있을까?&lt;/li&gt;
&lt;li&gt;함수 / 함수 포인터 / 펑터 / 람다식 모두 리턴 타입이 double, 매개변수도 double로 동일하다. 이를 call signature가 같다고 말한다. 이런 상황을 타개하기 위해 wrapper를 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657464640902&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double dub(double x) {return 2.0*x;}
double square(double x) {return x*x;}

function&amp;lt;double(double)&amp;gt; ef1 = dub;
function&amp;lt;double(double)&amp;gt; ef2 = square;
function&amp;lt;double(double)&amp;gt; ef3 = Fp(10.0);
function&amp;lt;double(double)&amp;gt; ef4 = Fp2(10.0;
function&amp;lt;double(double)&amp;gt; ef5 = [](double u){return u*u;);

use_f(y, ef1);
use_f(y, ef2);
use_f(y, ef3);
use_f(y, ef4);
use_f(y, ef5);&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Variadic Templates&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Template을 사용해서 매개변수에 상관없는 함수를 작성할 수 있다. 형태는 다음과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1657465386451&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T, typename... Args&amp;gt;
void show_list3(T value, Args... args)
{
	std::cout &amp;lt;&amp;lt; value &amp;lt;&amp;lt; &quot;, &quot;;
    show_list(args); // unpacking using recursion
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 예시처럼 재귀함수를 사용해서 unpacking을 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;References&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++ Primer Plus 6th, Chapter 18&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Unreal, C++</category>
      <author>_rien</author>
      <guid isPermaLink="true">https://ri3n.tistory.com/106</guid>
      <comments>https://ri3n.tistory.com/106#entry106comment</comments>
      <pubDate>Mon, 11 Jul 2022 00:11:11 +0900</pubDate>
    </item>
    <item>
      <title>[C++ Primer Plus] 16. The string Class and the Standard Template Library</title>
      <link>https://ri3n.tistory.com/105</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;대답해야 할 질문&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;smart pointer의 3가지 종류를 말하고 이를 설명하시오.&lt;/li&gt;
&lt;li&gt;shared_ptr에서 발생할 수 있는 circular referencing 상황을 설명하고 해결법을 말하시오.&lt;/li&gt;
&lt;li&gt;iterator의 5가지 종류를 말하고 차이점을 설명하시오.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;The string class&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지엽적인 내용 같아서 생략&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Smart Pointer Template Classes&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Smart Pointer(이하 '스마트 포인터')는 포인터에 몇개 기능이 더 있는 클래스 오브젝트다.&lt;/li&gt;
&lt;li&gt;기본적으로 dynamic memory allocation을 하면 delete를 해야 한다. 빠트리면 leak이 난다. 그러니까 leak을 피하는 방법은 프로그래머가 'delete를 해야지!'하고 기억하는 것이다. 이건 뭐 방법이라고는 할 수 없다.&lt;/li&gt;
&lt;li&gt;스마트 포인터는 자신가 죽을 때, 자기가 referencing하고 있는 메모리를 해제한다. 즉, delete를 더이상 신경 쓸 필요가 없다.&lt;/li&gt;
&lt;li&gt;스마트 포인터의 종류로는 auto_ptr(deprecated in C++11), unique_ptr(after C++11), shared_ptr(after C++11)가 있다. (C++11의 weak_ptr은 객체의 생애주기에 영향을 미치지 않으므로 뺐다. 책에서도 설명하지 않는다. 이 포스트에서는 따로 조사해서 설명할 예정)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Using Smart Pointers&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스마트 포인터를 쓰면 new를 이용해 객체를 생성한 이후 delete 시점 같은 것을 생각할 필요가 없다. 문법은 다음과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656228897758&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;memory&amp;gt;

auto_ptr&amp;lt;double&amp;gt; pd(new double); // pointer-to-double
shared_ptr&amp;lt;string&amp;gt; ps(new string); // pointer-to-string&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스마트 포인터는 dynamic memory allocation에만 적용된다. 즉, 아래 코드는 금지다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656229382410&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;std::string str = &quot;Hello, world!&quot;;
shared_ptr&amp;lt;std::string&amp;gt; sp(&amp;amp;str); // NO!!!&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Smart Pointer Considerations&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스마트 포인터는 왜 3개나 있을까? 왜 auto_ptr은 deprecated 됐을까? 문제는 아래 코드에서부터 시작한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656230058548&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;auto_ptr&amp;lt;string&amp;gt; ps (new string(&quot;Hello, world!&quot;));
auto_ptr&amp;lt;string&amp;gt; vocation;
vocation = ps; // 이 코드로 인해 string 하나를 스마트포인터 2개가 보고 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 코드가 그냥 저대로만 작동하면 string을 두 번 delete하게 되므로 에러가 발생한다. 이를 피하는 방법은 총 3가지다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;assignment operator를 쓰면 deep copy가 되게 한다. 즉, 위 예시에선 string을 복사하게 된다.&lt;/li&gt;
&lt;li&gt;ownership(이하 '소유권')의 개념을 도입한다. assignment operator를 쓰면 객체의 소유권이 넘어가게 하는 방식이다. 이러면 이전에 레퍼런싱하던 ps는 더이상 객체를 referencing 하지 못한다. 이게 auto_ptr과 unique_ptr이 쓰는 방식이다. 단, unique_ptr은 여기에 더 많은 제약을 둔다.&lt;/li&gt;
&lt;li&gt;스마트 포인터가 레퍼런싱하는 객체를 보는 스마트포인터가 몇개인지 셀 수 있도록 한다. 레퍼런싱 하는 스마트포인터가 0개가 될 때만 delete를 실행한다. 이 전략이 shared_ptr이 쓰는 방법이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;auto_ptr은 두번째 전략을 쓰고 있는데, 문제는 오용될 여지가 크다는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656230393594&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;memory&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;iostream&amp;gt;

int main(void)
{
	std::auto_ptr&amp;lt;std::string&amp;gt; arr[5] = {
		std::auto_ptr&amp;lt;std::string&amp;gt;(new std::string(&quot;str1&quot;)),
		std::auto_ptr&amp;lt;std::string&amp;gt;(new std::string(&quot;str2&quot;)),
		std::auto_ptr&amp;lt;std::string&amp;gt;(new std::string(&quot;str3&quot;)),
		std::auto_ptr&amp;lt;std::string&amp;gt;(new std::string(&quot;str4&quot;)),
		std::auto_ptr&amp;lt;std::string&amp;gt;(new std::string(&quot;str5&quot;)),
	};
	for (int i = 0; i &amp;lt; 5; ++i)
		std::cout &amp;lt;&amp;lt; *arr[i] &amp;lt;&amp;lt; std::endl;
	std::auto_ptr&amp;lt;std::string&amp;gt; catcher(arr[3]); // arr[3]의 ownership이 catcher로 넘어가버렸다.
	for (int i = 0; i &amp;lt; 5; ++i)
		std::cout &amp;lt;&amp;lt; *arr[i] &amp;lt;&amp;lt; std::endl;

	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 코드는 에러를 발생시킨다. catcher가 arr[3]의 ownership을 훔쳤기 때문이다. 이런 여지가 있으면 프로그래머는 auto_ptr을 쓸 때마다 제대로 된 값을 가리키고 있는지 확인해야 하는 번거로움이 생긴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Why unique_ptr Is Better than auto_ptr&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;unique_ptr이 어떻게 아래 상황을 피하는지 알아보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656230626381&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;unique_ptr&amp;lt;string&amp;gt; p3(new string(&quot;unique&quot;));
unique_ptr&amp;lt;string&amp;gt; p4;
p4 = p3;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;unique_ptr은 3번째 줄,&lt;b&gt; p4 = p3과 같은 코드를 받아들이지 않는다. 아예 컴파일이 안된다.&lt;/b&gt; 그로므로 unique_ptr은 auto_ptr에 비해 안전하다.&lt;/li&gt;
&lt;li&gt;하지만 = 연산자를 무턱대고 금지시키면 문제가 발생할 수 있다. 아래와 같이 unique_ptr 자체를 리턴해야 하는 상황이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656230758988&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;unique_ptr&amp;lt;string&amp;gt; demo(const char * s)
{
	unique_ptr&amp;lt;string&amp;gt; temp(new string(s));
    return temp;
}

int main()
{
	unique_ptr&amp;lt;string&amp;gt; ps = demo(&quot;Hello, world!&quot;); // 이 상황은 되면 좋겠다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;demo는 unique_ptr을 복사해서 반환할 것이다. 컴파일러는 demo의 반환값으로 temporary object인 unique_ptr을 반환한다. 어차피 임시 객체이니까 ps의 ownership에 문제가 생기지 않는다. 그래서 &lt;b&gt;unique_ptr은 위 코드를 인정한다.&lt;/b&gt; 이런 유연성 또한 unique_ptr이 auto_ptr에 비해 월등한 이유다.&lt;/li&gt;
&lt;li&gt;또한 move semantics를 사용해서 unique_ptr의 ownership을 옮길 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Selecting a Smart Pointer&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 한 오브젝트를 가리키는 여러 포인터가 필요하면 shared_ptr을 쓰고 그게 아니라면 unique_ptr을 쓰면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;weak_ptr (자체 추가)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;weak_ptr은 C++11에 추가된 포인터로 shared_ptr의 circular reference 문제를 해결하기 위해 등장했다. 해당 문제는 다음과 같은 상황에서 발생한다. (아래 코드는 &lt;a href=&quot;http://egloos.zum.com/sweeper/v/3059940&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;를 참조했다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656232427804&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;memory&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;

class Party;
typedef std::shared_ptr&amp;lt;Party&amp;gt; PartyPtr;
class User;
typedef std::shared_ptr&amp;lt;User&amp;gt; UserPtr;

class Party
{
public:
	void AddUser(UserPtr);
	std::vector&amp;lt;UserPtr&amp;gt; users;
};

class User
{
public:
	User(PartyPtr, std::string);
	PartyPtr partyPtr;
	std::string name;
};

int main(void)
{
	PartyPtr partyPtr(new Party);
	for (int i=0; i&amp;lt;5; ++i)
	{
		UserPtr up(new User(partyPtr, &quot;user&quot;));
		partyPtr-&amp;gt;AddUser(up);
	}

	for (int i=0; i&amp;lt;5; ++i)
	{
		std::cout &amp;lt;&amp;lt; partyPtr-&amp;gt;users[i]-&amp;gt;name &amp;lt;&amp;lt; std::endl;
	}

	// 여기서 party 객체가 사라지길 기대하지만 그러지 못한다.
	// 각 user 객체가 party에 대한 shared_ptr을 가지고 있기 때문이다.
	// party가 살아있기 때문에 user 객체들도 메모리를 떠돈다.
	partyPtr.reset();

	return 0;
}

void Party::AddUser(UserPtr up)
{
	users.push_back(up);
}

User::User(PartyPtr _pp, std::string _name)
{
	this-&amp;gt;partyPtr = _pp;
	this-&amp;gt;name = _name;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그룹 객체 - 소속 객체가 서로 포인터를 가지고 있는 것은 흔한 상황이다. 하지만 서로 shared_ptr을 가지고 있으면 메모리가 해제되지 못하고 쌓이는 상황이 발생한다. 이 문제를 해결해주는 것이 weak_ptr이다.&lt;/li&gt;
&lt;li&gt;weak_ptr은 shared_ptr을 통해서만 태어날 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656232622725&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;shared_ptr&amp;lt;string&amp;gt; sp(new string(&quot;Hello, shared!&quot;);
weak_ptr&amp;lt;string&amp;gt; wp = sp; // sp의 strong reference count가 아니라 weak reference count를 올린다.&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;weak_ptr은 shared_ptr의 객체 생명 주기에 영향을 주는 strong reference는 안 올리고 weak reference만 올린다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;The Standard Template Library&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;STL은 template을 활용한 cointainer, iterator, function objects, algorithm을 제공한다.&lt;/li&gt;
&lt;li&gt;STL은 OOP의 예시가 아니라 Generic Programming의 예시다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Generic Programming&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OOP가 프로그래밍에서 데이터를 강조한다면 Generic Programming은 알고리즘을 강조한다. 이러한 측면은 iterator를 보면 이해할 수 있다.&lt;/li&gt;
&lt;li&gt;double &lt;b&gt;array&lt;/b&gt;에서 특정 값을 찾아서 그 주소를 리턴해주는 함수를 만든다고 생각해보자. 코드는 아래와 같을 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656233621346&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double* find_arr(double* arr, int n, const double&amp;amp; val)
{
	for (int i=0; i&amp;lt;n; ++i)
    {
    	if (arr[i] == val)
        	return &amp;amp;arr[i];
    }
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이번엔 array가 아니라 list에서 find 함수를 만든다고 생각해보자. list는 node라는 오브젝트들이 연결된 형태라고 가정한다. 그럼 코드는 아래와 같을 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656233764648&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Node* find_list(Node* head, const double&amp;amp; val)
{
	Node* start;
    for (start=head; start!=0; start++)
    {
    	if (*start == val)
        	return start;
    }
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 함수는 실제 코드는 일부분 다르지만 결국 이루고자 하는 목표나 방식은 거의 비슷하다. 결국은 처음부터 끝까지 순회하면서 같은 값이면 리턴하는 방식이다. 그렇다면 하나의 함수로 해결할 순 없을까?&lt;/li&gt;
&lt;li&gt;이를 해결하기 위해 순회를 위한 클래스인 iterator를 만든다고 가정해보자. iterator는 다음 특징을 가져야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dereference를 통해 값에 접근할 수 있어야 한다. 예를 들어, *p를 통해 iterator가 가리키는 값이 무엇인지 알 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;iterator를 다른 iterator에 할당할 수 있어야 한다. 즉, p = q가 작동해야 한다.&lt;/li&gt;
&lt;li&gt;iterator가 서로 같은지 검증할 수 있어야 한다. 즉, p == q와 p != q가 작동해야 한다.&lt;/li&gt;
&lt;li&gt;iterator를 움직여서 container를 순회할 수 있어야 한다. p++ 또는 ++p를 통해 iterator가 다음 값을 가리키도록 할 수 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;위와 같은 iterator를 만들 수만 있다면 find_arr과 find_list는 아래 형태로 합칠 수 있다!&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656234127977&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;iterator find(iterator start, iterator end, const double &amp;amp; val)
{
	for (; start!=end; start++)
    {
    	if (*start == val)
        	return start;
    }
    return end;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;iterator는 위 조건만 맞춰진다면 pointer가 될 수도 있고 어떠한 오브젝트가 될 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Kinds of Iterators&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 알고리즘은 필요한 iterator의 조건이 다르다. 예를 들어, find 알고리즘의 iterator는 데이터를 읽는 기능은 필요하지만 쓰는 기능은 필요가 없다.&lt;/li&gt;
&lt;li&gt;STL은 이렇게 다른 iterator 조건을 다섯개로 나누어 제시하고 있다. 그들에게 필요한 조건을 표로 만들면 다음과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 170px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;Iterator Capability&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;Input&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;Output&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;Forward&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;Bidirectional&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;Random Access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;++i i++&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 34px;&quot;&gt;
&lt;td style=&quot;width: 16.6667%; height: 34px;&quot;&gt;Fixed and repeatable order&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 34px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 34px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 34px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 34px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 34px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;--i i--&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;i[n]&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;i + n&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;i - n&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;i += n&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;i -= n&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Concepts, Refinements, and Models&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;STL의 일부 기능인 iterator 같은 것들은 코드로 나타낼 수 없다. iterator의 기능 같은 것들은 type이 아니라 requirements에 가깝다. STL 알고리즘은 iterator가 요구사항을 만족할 때 제대로 작동한다. 이러한 요구사항을 concept라고 부른다.&lt;/li&gt;
&lt;li&gt;Concept는 상속의 개념이 있지만 코드로 이게 나타나지는 않는다. 그래서 Concept의 상속을 refinement라고 말한다. 예를 들어, Forward Iterator Concept는 OutputIterator와 InputIterator의 refinement다.&lt;/li&gt;
&lt;li&gt;그리고 이러한 Concept를 실제 코드로 나타낸 것을 model이라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Kinds of Containers&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;STL은 container concept와 container type을 전부 가지고 있다. container concept는 sequence container나 assotiative container와 같은 카테고리에 해당한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Container Concepts&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;container는 같은 종류의 오브젝트를 저장하는 오브젝트를 의미한다.&lt;/li&gt;
&lt;li&gt;container가 저장하는 오브젝트는 컨테이너에게 소유권이 있다. 즉, 컨테이너가 소멸할 때, 저장된 오브젝트들도 함께 소멸한다.&lt;/li&gt;
&lt;li&gt;컨테이너에 아무 자료형이나 저장할 순 없다. copy constructorable과 assignable의 두 특성을 모두 가진 타입만이 컨테이너에 저장될 수 있다.&lt;/li&gt;
&lt;li&gt;basic container는 데이터가 저장되는 순서 등을 보장하지 않는다. 하지만 sequence container나 assotiative container와 같은 refinements는 이를 보장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Sequences&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sequence containter엔 deque, forward_list, list, queue, priority_queue, stack, vector가 포함된다.&lt;/li&gt;
&lt;li&gt;sequence container는 각 원소들이 엄격한 순서대로 나열되어 있어야 한다. 즉, 메모리 상에서 순서가 맞춰져있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Associative Containers&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;value를 key와 함께 저장한다.&lt;/li&gt;
&lt;li&gt;Associative Container의 장점은 Sequence Container에 비해 검색이 빠르다는 점이다. 대신 원하는 인덱스로 이동하는 속도가 느리다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Function Objects (a.k.a. Functors)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;많은 STL 알고리즘들이 function objects(=functor)를 쓴다.&lt;/li&gt;
&lt;li&gt;functor는 () 연산자를 사용해서 마치 함수처럼 사용할 수 있는 오브젝트다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656936284649&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Linear
{
private:
	double slope;
    double y0;
public:
	Linear(double sl_ = 1, double y_ = 0)
    	: slope(sl_), y0(y_) {}
    double operator() (double x) { return y0 + slope * x; }
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Functor Concepts&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;STL에서 Functor는 3가지 정도로 나뉜다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;generator: 매개변수 없이 호출할 수 있는 functor&lt;/li&gt;
&lt;li&gt;unary function: 매개변수 하나로 호출하는 functor&lt;/li&gt;
&lt;li&gt;binary function: 매개변수 두개로 호출하는 functor&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;References&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://egloos.zum.com/sweeper/v/3059940&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://egloos.zum.com/sweeper/v/3059940&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1656231506693&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[TR1] weak_ptr&quot; data-og-description=&quot;1. shared_ptr shared_ptr의 내용은 다음 링크를 참고하기 바라며, 특히 3-9 Circular reference 챕터를 자세히 읽어보기 바란다. (위 링크엔 shared_ptr의 circular reference에 대한 예제가 포함되어 있다) 2. weak_ptr sh&quot; data-og-host=&quot;egloos.zum.com&quot; data-og-source-url=&quot;http://egloos.zum.com/sweeper/v/3059940&quot; data-og-url=&quot;http://egloos.zum.com/sweeper/v/3059940&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Yd8RD/hyOSUpuZc5/kNkBo8C9GzJJIsLVa90U21/img.jpg?width=250&amp;amp;height=250&amp;amp;face=0_0_250_250&quot;&gt;&lt;a href=&quot;http://egloos.zum.com/sweeper/v/3059940&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://egloos.zum.com/sweeper/v/3059940&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Yd8RD/hyOSUpuZc5/kNkBo8C9GzJJIsLVa90U21/img.jpg?width=250&amp;amp;height=250&amp;amp;face=0_0_250_250');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[TR1] weak_ptr&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. shared_ptr shared_ptr의 내용은 다음 링크를 참고하기 바라며, 특히 3-9 Circular reference 챕터를 자세히 읽어보기 바란다. (위 링크엔 shared_ptr의 circular reference에 대한 예제가 포함되어 있다) 2. weak_ptr sh&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;egloos.zum.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++ Primer Plus 6th, Chapter 16&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Unreal, C++</category>
      <author>_rien</author>
      <guid isPermaLink="true">https://ri3n.tistory.com/105</guid>
      <comments>https://ri3n.tistory.com/105#entry105comment</comments>
      <pubDate>Mon, 4 Jul 2022 22:52:09 +0900</pubDate>
    </item>
    <item>
      <title>[C++ Primer Plus] 15. Friends, Exceptions, and More</title>
      <link>https://ri3n.tistory.com/104</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;대답해야 할 질문들&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Friend class와 Friend member function의 사용법이 무엇인가?&lt;/li&gt;
&lt;li&gt;Nested class를 쓰는 이유는 무엇인가?&lt;/li&gt;
&lt;li&gt;return과 throw의 차이점이 무엇인가?&lt;/li&gt;
&lt;li&gt;exception을 레퍼런스로 catch하는 이유는 무엇인가?&lt;/li&gt;
&lt;li&gt;undefined exception과 unexpected exception이 무엇인지 설명하고 어떻게 해결할 수 있는지 설명하시오.&lt;/li&gt;
&lt;li&gt;RTTI의 세가지 구성요소는 무엇인가?&lt;/li&gt;
&lt;li&gt;C++의 explicit casting을 설명하시오.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Friends&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이제까지 friend function만 배웠지만, friend class도 가능하다.&lt;/li&gt;
&lt;li&gt;friend class가 마치 OOP를 해칠 것만 같은 느낌을 주지만 그렇지 않다. 그 이유는 2개다. 먼저 friend 관계는 외부에 노출되지 않는다. 두번째로 friend라고 해도 모든 friend 클래스에게 전적으로 멤버를 공개하는 것이나 아니라 friend 클래스의 특정 함수에게만 공개할 수도 있다. 즉, friend 클래스도 결국은 interface로서 작동할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Friend Classes&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TV와 리모컨 클래스가 있다고 생각해보자. 리모컨을 통해 TV를 동작시킬테니, 두 클래스 사이에 어떤 관계가 있긴할 것이다. 그런데 대체 무슨 관계라고 할 수 있을까? Is-A, Has-A 관계에 해당되지 않는다. 이 때 사용할 수 있는 것이 friend class다.&lt;/li&gt;
&lt;li&gt;아래 예제는 TV 클래스에서 리모컨 클래스를 Friend로 지칭해서 리모컨 클래스가 TV 클래스의 protected, provate 멤버에 접근할 수 있도록 해주는 코드다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656054695064&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Tv
{
public:
	friend class Remote;
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Friend Member Functions&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그런데 직접 리모컨 클래스를 구현해보니, 왠만한 경우에 Tv 클래스의 public 멤버만 쓴다는 것을 깨달았다. 실제로 Tv 클래스의 Private 멤버를 쓰는 일은 리모컨의 set_channel()에서만 일어났다. 이 경우에 Friend Member Functions을 지시할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656054867508&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Tv
{
public:
	friend void Remote::set_channel(Tv &amp;amp; t, int c);
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 때, 실제로 코드를 써보면 에러를 맞닥뜨리게 된다. Tv 클래스와 Remote 클래스의 정의 순서 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656055018473&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. TV class가 friend member function에서
// Remote를 참조하기 때문에 Remote가 클래스임을 컴파일러에게 알려줘야 한다.
// 2. Remote class도 멤버 함수에서 TV 클래스를 쓰기 때문에 TV보다 아래에 있어야 한다.

class TV
{
...
}

class Remote
{
...
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이를 타파하기 위해 &lt;b&gt;forward declaration&lt;/b&gt;을 헤더에서도 써야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656055065234&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Tv;

class Remote
{
...
}

class Tv
{
...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Nested Classes&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++에서는 클래스 정의부 안에서 다른 클래스를 또 정의할 수 있다. 이 때, 그 안에 있는 클래스를 Nested Class라고 부른다.&lt;/li&gt;
&lt;li&gt;Nested Class는 Containment와 다르다. Containment는 어떤 클래스의 오브젝트를 멤버로 갖는 것이고 Nested Class는 아예 클래스 정의 자체를 안에서 하는 것이다. 애초에 멤버라고는 할 수 없다.&lt;/li&gt;
&lt;li&gt;그래서 Nested Class를 바깥에서 접근하려면 품고 있는 클래스가 정의를 public section에서 해줘야 한다.&lt;/li&gt;
&lt;li&gt;Nested Class를 쓰는 이유는 주로 이름 충돌을 피하기 위해서다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Nesting in a Template&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Template Class에서 Nested Class를 정의할 때, 뭔가 어려움이 생길까? 답은 '아니오'다. 실제로 Template에 들어온 type parameter를 Nested Class의 정의부에 사용할 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656055463828&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template &amp;lt;class Item&amp;gt;
class QueueTP
{
private:
	class Node
    {
    public:
    	Item item; // Nesting class의 type parameter 사용
    ...
    }
...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Exceptions&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원하지 않는 값이 들어오거나 메모리 할당에 실패하는 등 에러 상황이 발생했을 때, 프로그래머의 대처는 3가지다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;exit() 또는 abort()를 사용해서 프로그램을 꺼버린다.&lt;/li&gt;
&lt;li&gt;return을 할 때, -1과 같은 에러 숫자를 리턴해서 함수 바깥에서 처리한다.&lt;/li&gt;
&lt;li&gt;exception을 throw한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Exception Mechanism&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Exception은 총 3가지 부분으로 실행된다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;exception을 throw한다.&lt;/li&gt;
&lt;li&gt;exception을 catch한다.&lt;/li&gt;
&lt;li&gt;try 블럭을 설정한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;throw 구문은 jump라고 이해하면 쉽다.&lt;/b&gt;&lt;/u&gt; 실행해야 하는 명령줄을 try 블럭 끝 줄로 쭉 이동시킨다.&lt;/li&gt;
&lt;li&gt;catch 블록은 throw된 exception과 exception handler를 받아들여&lt;/li&gt;
&lt;li&gt;try 블록은 exception이 발생할 수 있는 구문을 묶어준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Using Objects as Exceptions&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;exception 시스템의 좋은 점 중 하나는 다양한 exception type을 throw할 수 있어서 예외에 따라 다른 대처가 가능하다는 점이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656056815046&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class bad_hmean
{
private:
	double v1;
    double v2;
public:
	void mesg();
}

...

if (a == -b)
	throw bad_hmean();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Exception Specifications Meet C++11&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제로 꽤 쓸만해보이는 원칙이 현장에선 먹히지 않는 법이다. C++98의 exception specification이 그 예다.&lt;/li&gt;
&lt;li&gt;exception specification은 C++98에 도입됐고 C++11에서 deprecated된 기능이다. 문법은 아래와 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656057014324&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double harm(double a) throw(bad_thing); // bad_thing 에러를 throw한다.
double marm(double) throw(); // 어떤 에러도 throw하지 않는다.&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 구문은 두 가지 역할을 한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;프로그래머에게 해당 함수가 어떤 예외를 던질 수 있는지 명시한다.&amp;nbsp;&amp;rarr; 그냥 주석으로 하면 된다.&lt;/li&gt;
&lt;li&gt;컴파일러에게 정말로 이 약속을 지켰는지 검사할 수 있게 한다.&amp;nbsp;&amp;rarr; 때때로 변하는 코드 때문에 사실 큰 의미가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;결국 C++11에서 exception specification은 사라졌고 이를 대신할 새로운 키워드가 생겼다. 바로 &lt;b&gt;noexcept&lt;/b&gt;다. noexcept는 throw()와 동일한 의미로 해당 함수가 던지는 exception이 없다는 의미다. 이 키워드는 문맥적인 의미뿐만이 아니라 컴파일러가 더 효율적으로 컴파일할 수 있도록 도와준다. (try 구문에서 최적화하는 듯?)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656057272351&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double marm() noexcept;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Unwinding the Stack&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;throw 구문을 jump라고 이해하라고 위에서 말했다. 하지만 이게 어떻게 가능할까? 이제까지 함수의 실행순서에 관여하는 것은 return 뿐이었다. 함수는 호출되면 파라미터를 포함해 지역변수들을 모두 Stack에 생성한다. 그 후, return이 되면 Stack에 있던 메모리를 return adress까지 모두 해제한다. 그렇다면 throw는?&lt;/li&gt;
&lt;li&gt;throw가 되면 return과는 달리 return address가 아니라 try의 return address까지 모든 메모리를 해제한다. 이를 Unwinding the Stack이라고 한다. 그래서 Stack에 할당된 메모리들이 안전하게 해제될 수 있는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRDT5t/btrFFiGZeir/yvWLhlqBsBorEkPSfAdkq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRDT5t/btrFFiGZeir/yvWLhlqBsBorEkPSfAdkq0/img.png&quot; data-alt=&quot;출처: C++ Primer Plus 6th&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRDT5t/btrFFiGZeir/yvWLhlqBsBorEkPSfAdkq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRDT5t%2FbtrFFiGZeir%2FyvWLhlqBsBorEkPSfAdkq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;490&quot; height=&quot;376&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: C++ Primer Plus 6th&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;More Exception Features&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;throw가 return과는 다른 점이 있는데, throw는 항상 exception 오브젝트의 복사본을 throw한다는 것이다.&lt;/li&gt;
&lt;li&gt;그렇게 되면 왜 굳이 아래와 같은 문법을 쓰는지 이해가 안될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656057732719&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
	super();
}
catch(problem &amp;amp;p) { // 왜 굳이 레퍼런스로 받아낼까? 어차피 복사해서 주는데?
	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그 이유는 상속 때문이다. 던지는 exception 오브젝트는 높은 확률로 derived-class일 것이다. 이 때, 부모 클래스의 레퍼런스로 받으면 그 자식들까지 전부 받아낼 수 있다.&lt;/li&gt;
&lt;li&gt;또한 만약 모든 exception을 일단 받아내고 싶으면 다음과 같은 문법도 (의외로) 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656057830845&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;catch (...) { //statement }&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;The exception class&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++의 exception 기능은 exception을 언어 자체에서 지원하기 위해서다. 그냥 이렇게만 지원하면 각 프로그램들은 exception을 규격화하는 일을 해야한다. std::exception은 그 규격을 정해주는 역할을 한다.&lt;/li&gt;
&lt;li&gt;std::exception 클래스를 쓰고 싶으면 &amp;lt;exception&amp;gt; 헤더를 include하고 다음과 같이 예외 클래스를 정의하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656058050618&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class bad_gmean : std::exception
{
public:
	const char * what() { return &quot;bad arguments to gmean()&quot;; }
}


...

try {
...
}
catch(std::exception&amp;amp; e) {
	std::cerr &amp;lt;&amp;lt; e.what() &amp;lt;&amp;lt; std::endl;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stdexcept는 std::exception 클래스보다 좀더 상세하게 예외를 정의한다. stdexcept는 예외 클래스를 크게 logic_error와 runtime_error 클래스로 나누고 자식 클래스를 두고 있다. 관계는 다음과 같다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;logic_error family
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;domain_error&lt;/li&gt;
&lt;li&gt;invalid_error&lt;/li&gt;
&lt;li&gt;length_error&lt;/li&gt;
&lt;li&gt;out_of_bounds&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;runtime_error
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;range_error&lt;/li&gt;
&lt;li&gt;overflow_error&lt;/li&gt;
&lt;li&gt;underflow_error&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;The bad_alloc Exception and new&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;new 연산을 통해 메모리를 할당하려는데, 여기에 실패하면 bad_alloc exception을 던진다. 하지만 아래 구문을 사용하면 에러를 안던지게끔 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656058655843&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Big * pb;

pb = new (std::nothrow) Big[10000];
if (pb == 0)
{
	cout &amp;lt;&amp;lt; &quot;Could not allocate memory. Bye.\n&quot;;
    exit(EXIT_FAILURE);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;new에 실패했기 때문에 pb 값이 0으로 된 것을 주목하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;When Exceptions Go Astray&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Exception을 잡는데 실패하는 경우는 2가지다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;catch 구문에 걸리지 않는 type의 예외&amp;nbsp;&amp;rarr; 해당 exception은 &lt;b&gt;unexpected&lt;/b&gt; exception로 branded 된다.&lt;/li&gt;
&lt;li&gt;try를 하지 않았는데, throw를 하는 경우 &amp;rarr; 해당 exception은 &lt;b&gt;undefined&lt;/b&gt; exception로 branded 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;기본적으로 두 예외가 발생해버리면 abort()가 된다. 정확히 말하자면 &lt;b&gt;abort() 함수를 바로 호출하는게 아니라 terminate() 함수를 부르고 이 함수가 기본적으로 내부에서 abort()를 호출한다.&lt;/b&gt; 이러한 방식을 바꾸기 위해서 다음 함수들이 &amp;lt;exception&amp;gt;헤더에 정의되어 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656058946841&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef void (*terminate_handler)();
terminate_handler set_terminate(terminate_handler f) noexcept;
void terminate() noexcept;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 함수들을 override하면 바로 abort하는 디폴트 세팅을 바꿀 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656059103667&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void myQuit()
{
	cout &amp;lt;&amp;lt; &quot;Terminating due to uncaught exception\n&quot;;
}

set_terminate(myQuit);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Exception Cautions&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;exception은 바로 프로그램을 끄거나 에러값을 리턴하는 것보다 좋아보이지만 trade-off가 있다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;먼저 프로그램의 사이즈를 크게 만들고 속도를 늦춘다.&lt;/li&gt;
&lt;li&gt;template이나 동적 메모리 할당에 대응하기 힘들다.&lt;/li&gt;
&lt;li&gt;바로 꺼버리는 경우가 디버깅 등을 위해 좋을 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Runtime Type Identification (RTTI)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RTTI의 목적은 객체의 타입을 런타임 중에 추측할 수 있는 표준 방법을 제시하는 것이다.&lt;/li&gt;
&lt;li&gt;RTTI가 필요한 경우가 언제일까?
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;class 타입에 따른 메소드를 호출하기 위해서&amp;nbsp;&amp;rarr; virtual function으로 풀 수 있다면 RTTI를 안쓰는게 낫다.&lt;/li&gt;
&lt;li&gt;debugging을 목적으로 객체의 타입을 알아내기 위해서 &lt;span&gt;&amp;rarr;&lt;span&gt; 이건 RTTI 밖에 답이 없다.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;How Does RTTI Work?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RTTI는 세가지 구성요소로 작동된다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;dynamic_cast 연산자&lt;/li&gt;
&lt;li&gt;typeid 연산자&lt;/li&gt;
&lt;li&gt;type_info 구조체&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;The dynamic_cast operator&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dynamic_cast 연산자가 RTTI 컴포넌트 중에서 가장 많이 쓰인다. 이 연산자는 해당 객체가 어떤 타입인지 알려주지 않는다. 그보다는 특정 포인터가 &lt;span&gt;해당 객체를&lt;/span&gt; 안전하게 가리킬 수 있는지 검사한다. 만약 그럴 수 없다면 0을 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656059716319&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Superb * pm = dynamic_cast&amp;lt;Superb *&amp;gt;(pg);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;The typeid operator and type_info Class&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;typeid 연산자는 클래스의 이름이나 오브젝트를 받아서 type_info 오브젝트의 레퍼런스를 반환한다.&lt;/li&gt;
&lt;li&gt;type_info 클래스는 name() 메소드가 있는데, 이를 이용해서 디버깅을 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Type Cast Operators&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Bjarne Stroustrup은 C type cast operator가 너무 느슨하다고 말한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656060445204&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct Data {...};
struct Junk {...};

Data d = {2.5e33, 3.5e-19, 20.2e32};
// C에서는 아래 코드의 말도 안되는 캐스팅이 전부 바로 가능하다.
char* pch = (char *) (&amp;amp;d);
char ch = char (&amp;amp;d);
Junk * pj = (Junk *) (&amp;amp;d);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그래서 C++은 좀더 규칙을 가진 캐스팅 방식을 지원한다. 이는 4가지 연산자를 통해 구현됐다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656060568211&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dynamic_cast
const_cast
static_cast
reinterpret_cast&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;const_cast&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;const_cast는 해당 type에 const를 없애거나 추가할 때 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656060734597&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;High bar;
const High * pbar = &amp;amp;bar;

High * pb = const_cast&amp;lt;High *&amp;gt;(pbar);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이런 기능은 const를 잠시 떼놓아야 할 때 사용된다. const_cast는 기존 캐스팅을 통해서도 가능한데, 기존 캐스팅은 아예 type까지 바꿀 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656060830161&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;High * pb = (High *)(pbar);
Low * pl = (Low *)(pbar); // 다운 캐스팅과 컨스트 캐스팅을 동시에 할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;const_cast만 보면 const 특성을 아예 지워버리는 것 같은데, 실제로는 그렇지 않다. 다음 예제를 보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1656060986480&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main()
{
	int pop1 = 38383;
    const int pop2 = 2000;
    
    change(&amp;amp;pop1, -103); // pop1은 캐스팅된 const이기 때문에 값이 바뀐다.
    change(&amp;amp;pop2, -103); // 이 때, pop2는 진짜 const이기 때문에 값이 바뀌지 않는다.
    
    cout &amp;lt;&amp;lt; pop1 &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; pop2 &amp;lt;&amp;lt; endl;
}

void change(const int * ptr, int n)
{
	int * pc;
    
    pc = const_cast&amp;lt;int *&amp;gt;(ptr);
    *pc += n;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 변수의 const 특성 자체를 없애는 것이 아니라 임시로 const가 붙었을 때, 이를 없애주는 역할인 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;static_cast&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;static_cast는 계층 관계에 있는 클래스 간 형변환이 가능하다. 즉, High -&amp;gt; Low 또는 Low -&amp;gt;High 모두 가능하다. 하지만 Low -&amp;gt; Pond 는 계층관계가 아니므로 안된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;reinterpret_cast&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;reinterpret_cast는 계층 관계를 벗어나서 자유롭게-위험하게 캐스팅이 가능하다. Low -&amp;gt; Pond도 reinterpret_cast라면 가능하다.&lt;/li&gt;
&lt;li&gt;다만 완전히 자유로운 것은 아니다. 예를 들어, pointer 타입을 floating-type이나 short 타입으로 형변환할 수 없다. 모든 정보를 담을 수 없기 때문이다.&lt;/li&gt;
&lt;li&gt;reinterpret_cast는 주로 로우 레벨에서 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;References&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++ Primer Plus 6th, Chapter 15&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Unreal, C++</category>
      <category>C++</category>
      <category>const_cast</category>
      <category>CPP</category>
      <category>dynamic_cast</category>
      <category>reinterpret_cast</category>
      <category>static_cast</category>
      <author>_rien</author>
      <guid isPermaLink="true">https://ri3n.tistory.com/104</guid>
      <comments>https://ri3n.tistory.com/104#entry104comment</comments>
      <pubDate>Fri, 24 Jun 2022 18:24:54 +0900</pubDate>
    </item>
    <item>
      <title>[C++  Primer Plus] 14. Reusing Code in C++</title>
      <link>https://ri3n.tistory.com/103</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;대답해야 할 질문들&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;has-a 관계를 가지는 두 가지 방법은 무엇인가?&lt;/li&gt;
&lt;li&gt;다이아몬드 상속을 할 때, 신경써줘야하는 부분은 무엇인가?&lt;/li&gt;
&lt;li&gt;template class에서 implicit/explicit instantiation과 specialization의 문법은 무엇인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Classes with Object Members&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스가 오브젝트 멤버를 가지는 방법은 2가지다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;오브젝트를 변수로 선언한다. (containment)&lt;/li&gt;
&lt;li&gt;protected or private으로 상속한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Private Inheritance&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상속할 때, Private으로 상속하면 base-class의 public 멤버와 protected 멤버가 derived-class의 private 멤버가 된다.&lt;/li&gt;
&lt;li&gt;즉, 인터페이스가 똑같이 이어지는 게 아니기 때문에 Is-a 관계가 아니라 Has-a 관계다.&lt;/li&gt;
&lt;li&gt;예를 들어, Student 클래스가 std::string 클래스를 private 상속받으면 std::string처럼 문자열을 저장할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Initialization Base-Class Components&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Containment의 생성자(std::string)와 Private 상속의 생성자의 생김새 차이를 유의하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655984168981&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Containment
Student(const char * str, const double * pd, int n)
	: name(str), scores(pd, n) {}
// Private Inheritance
Student(const char * str, const double * pd, int n)
	: std::string(str), ArrayDb(pd, n) {}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Accessing Base-Class Method&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Base-Class의 메소드에 접근하려면 scope-resolution operator(::)를 써야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655984298683&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double Student::Average() const
{
	if (ArrayDb::size() &amp;gt; 0)
    	return ArrayDb::sum()/ArrayDb::size();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 Base-Class 오브젝트의 주소값 자체에 접근하려면 어떻게 하면 좋을까? 답은 &lt;b&gt;형변환&lt;/b&gt;이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655984407821&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ostream&amp;amp; operator&amp;lt;&amp;lt;(ostream&amp;amp; os, const Student&amp;amp; stu)
{
	os &amp;lt;&amp;lt; &quot;Scores for &quot; &amp;lt;&amp;lt; (const String &amp;amp;) stu &amp;lt;&amp;lt; &quot;:\n&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Containment or Private Inheritance?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그렇다면 containment와 Private Inheritance 중에 뭘 사용하는 게 좋을까?&lt;/li&gt;
&lt;li&gt;결론부터 말하자면 &lt;b&gt;특별한 이유가 없으면 containment&lt;/b&gt;다.&lt;/li&gt;
&lt;li&gt;private Inheritance를 써야되는 경우는 다음과 같다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;protected 멤버에 접근해야 하는 경우&lt;/li&gt;
&lt;li&gt;해당 클래스의 virtual function을 수정하고 싶은 경우&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Redefining Access with using&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;private instance로 has-a 관계를 만들었다면 base-class의 함수들이 전부 감춰지므로 래핑 함수를 만들게 되는 경우가 있다. 예를 들면 다음과 같은 함수가 생기는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655985001021&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double Student::sum() const // public Student's method
{
	return std::valarray&amp;lt;double&amp;gt;::sum();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 경우엔 using 문법을 사용해서 쉽게 선언할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655985111178&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student: private std::string, private std::valarray&amp;lt;double&amp;gt;
{
public:
	using std::valarray&amp;lt;double&amp;gt;::min;
    using std::valarray&amp;lt;double&amp;gt;::max;
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이전엔 using 키워드를 쓰지 않아도 됐었는데, 이는 deprecated 문법이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Multiple Inheritance&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다중상속은 다음과 같은 문법이다. 각자마다 public 한정자를 줘야하는 걸 주목하자. 안주면 private된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655985398818&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class SingingWaiter : public Waiter, public Singer {...}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제는 Waiter와 Singer가 같은 base-class를 두는 경우다. 따로 처리를 안해주면 Waiter의 Worker 오브젝트 하나, Singer의 Worker 오브젝트 하나, 이렇게 두 개 오브젝트가 생긴다. 이런 경우가 의도된 것일 수 있는데, 보통 그렇진 않으므로 이 경우엔 어떻게 처리해야 하는지 알아보자.&lt;/li&gt;
&lt;li&gt;위 문제를 해결하려면 Waiter와 Singer가 전부 virtual 상속을 해줘야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655985656951&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Singer : virtual public Worker {...};
class Waiter : public virtual Worker {...};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;virtual? 동적 바인딩이기 때문인가? 라고 생각하기 쉽지만 절대 그런게 아니다.&lt;/b&gt; runtime에 상속 판단이 되는게 아니다. virtual인 이유는 좀더 정치적인 이유에 가깝다. C++ 커뮤니티가 새로운 키워드 추가에 매우 회의적이었기 때문에 이미 있는 키워드를 썼던 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;New Constructor Rules&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MI는 생성자에서도 신경써줘야 하는 부분이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655986428179&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SingingWaiter(const Worker &amp;amp; wk, int p = 0, int v = Singer::other)
	: Waiter(wk, p), Singer(wk, v) // flawed&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 코드는 제대로 작동하지 않는다. Waiter와 Singer 클래스의 생성자가 내부에서 base-class의 생성자를 호출할텐데, 이게 작동하지 않기 때문이다. 그 이유는 virtual 상속인 경우, 컴파일러가 해당 호출을 무효로 처리하기 때문이다. 그래서 따로 base-class의 생성자를 호출해줘야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655986631523&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SingingWaiter(const Worker &amp;amp; wk, int p = 0, int v = Singer::other)
	: Worker(wk), Waiter(wk, p), Singer(wk, v)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Which Method?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MI는 base-class들의 함수를 호출할 때에 주의해야 한다. 함수 이름이 겹치는 경우가 많기 때문이다. scope resolution operator를 사용해서 정확히 어떤 base class의 함수를 쓸건지 말해줘야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655986770620&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;newhire.Singer::Show();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mixed Virtual and Nonvirtual Bases&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네 개의 클래스를 상속받는데, 두 개가 virtual 상속이고 두 개가 일반 상속이면 총 몇개의 base 클래스가 생길까?&lt;/li&gt;
&lt;li&gt;정답은 3개다. virtual 끼리 하나, 일반 상속이 하나씩해서 3개다. virtual 끼리는 하나를 공유한다고 생각하면 쉽다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Class Templates&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Class에서 Template을 적용하려면 다음 문법이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655987556931&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename Type&amp;gt;
class Stack
{
private:
	...
    Type items[MAX];
    ...
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;멤버 함수 구현부에도 template 키워드를 써줘야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655987611377&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template &amp;lt;class Type&amp;gt;
bool Stack&amp;lt;Type&amp;gt;::push(const Type &amp;amp; item)
{
...
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Implicit Instantiation, explicit Instantiation, explicit specialization은 template 함수와 유사하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655988048196&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ArrayTP&amp;lt;int, 100&amp;gt; stuff; // implicit instantiation
template class ArrayTP&amp;lt;int, 100&amp;gt;; // explicit instantiation

template &amp;lt;&amp;gt;
class SortedArray&amp;lt;const char *&amp;gt;
{
...
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;explicit specialization에서는 partial specialization도 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655988141148&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;class T1, class T2&amp;gt; class Pair {...};
template&amp;lt;class T1&amp;gt; class Pair&amp;lt;T1, int&amp;gt; {...}; // partial specialization
template&amp;lt;&amp;gt; class Pair&amp;lt;int, int&amp;gt; {...}; // specialization&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;References&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++ Primer Plus 6th, Chapter 14&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Unreal, C++</category>
      <category>C++</category>
      <category>CPP</category>
      <category>다이아몬드 상속</category>
      <author>_rien</author>
      <guid isPermaLink="true">https://ri3n.tistory.com/103</guid>
      <comments>https://ri3n.tistory.com/103#entry103comment</comments>
      <pubDate>Thu, 23 Jun 2022 21:45:21 +0900</pubDate>
    </item>
    <item>
      <title>[C++ Primer Plus] 13. Class Inheritance</title>
      <link>https://ri3n.tistory.com/102</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;대답할 질문&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상속을 통해 어떤 일을 할 수 있는가?&lt;/li&gt;
&lt;li&gt;derived-class의 constructor와 destructor는 어떻게 작동하는가?&lt;/li&gt;
&lt;li&gt;Is-a relationship은 무엇인가?&lt;/li&gt;
&lt;li&gt;dynamic binding이 default가 아닌 이유가 무엇인가?&lt;/li&gt;
&lt;li&gt;virtual function은 어떻게 작동하는가?&lt;/li&gt;
&lt;li&gt;derived-class에서 함수를 재정의할 때 무엇을 유의해야 하는가?&lt;/li&gt;
&lt;li&gt;Abstract Base Class는 어떻게 만들 수 있는가? 왜 만드는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Class Inheritance&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OOP의 목적 중 하나가 코드 재사용성이다. Inheritance(상속)는 이 특성을 강화한다.&lt;/li&gt;
&lt;li&gt;C library를 생각해보자. 따로 소스 코드가 제공되지 않는 라이브러리라면 우리는 특정 함수를 입맛대로 바꾸기 어려울 것이다. 하지만 C++은 Class를 제공함으로써 이를 해결한다. Class Inheritance를 통해 우리는 세가지 일을 할 수 있다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;기존 클래스에 새로운 기능을 추가한다.&lt;/li&gt;
&lt;li&gt;기존 클래스의 기능을 변경한다.&lt;/li&gt;
&lt;li&gt;클래스에 새로운 데이터를 추가한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Beginning with a Simple Base Class&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상속 관계에서 기존 클래스를 base class,&amp;nbsp; 상속받는 클래스를 derived class라고 부른다.&lt;/li&gt;
&lt;li&gt;derived class는 base class의 private 멤버에게 접근할 수 있는 권한이 없다. 그렇다보니 derived class의 생성자를 만들 때, 애매한 경우가 생길 수 있는데, 이는 base class의 생성자를 통해 해결할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655878028146&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;RatedPlayer::RatedPlayer(unsigned int r, const string &amp;amp; fn, const string&amp;amp; ln, bool ht) : TableTennisPlayer(fn, ln, ht)
{
	rating = r;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;근본적으로는 결국 base class의 public method를 통해 초기화하는 것이다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;derived class의 constructor는 다음과 같이 작동한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;base class의 default constructor가 호출된다.&lt;/li&gt;
&lt;li&gt;derived-class의 constructor는 base-class information을 매개변수로 받았을테니, 이를 base class의 생성자를 호출해서 데이터를 저장한다.&lt;/li&gt;
&lt;li&gt;derived-class에서 추가된 data를 저장한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;destructor는 반대로 작동한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Special Relationships Between Derived and Base Classes&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;base-class와 derived class는 특별한 관계성을 가진다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;먼저 base-class의 메소드 중에서 private이 아닌 메소드를 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;derived class의 포인터나 레퍼런스는 base-class의 포인터나 레퍼런스로 implicit conversion할 수 있다. 이 rule relaxion은 단방향이다. 즉, base-class의 포인터나 레퍼런스는 derived class의 포인터나 레퍼런스로 implicit conversion이 불가하다. dynamic_cast를 통한 explicit conversion은 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Inheritance: An Is-a Relationship&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Inheritance는 기본적으로 Is-a 관계를 만들어낸다. Is-a 관계는 Is-a-kind-of의 뜻이라고 생각하면 더 편하다.&lt;/li&gt;
&lt;li&gt;포도나 오렌지는 과일이다. 하지만 lunch는 fruit를 포함하겠지만 과일이라고는 할 수 없다.&lt;/li&gt;
&lt;li&gt;다시 강조하자면, Inheritance는 클래스의 특성을 추가할 뿐이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;The Need for Virtual Destructors&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약, destructor가 virtual이 아니라면 base-class의 destructor만 부르게 되는 상황이 발생한다. 만약 derived-class의 생성자에서 new라도 했다가는 leak이 발생하는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Static and Dynamic Binding&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;function call이 있을 때, 어떤 function을 실행해야 하는가?&quot;는 컴파일러가 대답해야 한다. C에서는 이걸 결정하는 게 아주 쉬웠다. 이름만 찾아서 매칭해주면 끝이었다. C++은 다르다. 함수 오버로딩 등이 있기 때문이다. 하지만 함수 오버로딩 등을 포함해서 왠만해서 이런 매칭은 컴파일 타임에 일어난다.&lt;/li&gt;
&lt;li&gt;함수 콜과 실제 함수 코드를 매칭해주는 것을 Binding이라고 부른다. Binding이 컴파일 타임에 일어나면 Static Binding, 런타임에 일어나면 Dynamic Binding이다. virtual function은 Dynamic Binding이다.&lt;/li&gt;
&lt;li&gt;Dynamic Binding은 런타임에 타입에 따른 함수를 매칭해주기 때문에 편하다. 그렇다면 두가지 질문이 생길 것이다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;그런데 왜 Dynamic Binding이 Default가 아닌가?&lt;/li&gt;
&lt;li&gt;Virtual function은 어떻게 작동하는가?&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Why Two Kinds of Binding and Why Static Is the Default?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기엔 두 가지 이유가 있다. 효율성(efficiency)과 클래스 디자인(a conceptual model)이다.&lt;/li&gt;
&lt;li&gt;Virtual Function의 작동 방식을 살펴보면 알겠지만, Dynamic Binding은 기존 함수 호출에 한단계가 더 추가되면서 오버헤드가 발생한다. Static Binding은 이를 컴파일에 다 해결해놓기 때문에 비용이 더 싸다. Stroustrup은 C++ 원칙 중 하나로 '사용하지 않는 기능으로 인한 비용을 지불하지 않는다'(you shouldn't have to pay for features you don't use)를 말했다.&lt;/li&gt;
&lt;li&gt;클래스 디자인 측면에서 virtual을 함수에 넣지 않음으로써 이 함수가 재정의되기를 원하지 않는다는 의도를 강조할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;How Virtual Functions Work&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++은 Virtual 함수의 특징을 서술하지만 어떻게 작동해야 하는지는 컴파일러에게 맡긴다. 그래서 컴파일러마다 작동방식은 다르다.&lt;/li&gt;
&lt;li&gt;보통 컴파일러는 virtual function을 관리하기 위해서 각 오브젝트마다 숨겨진 멤버 변수를 추가한다. 이 변수는 virtual function들을 담은 table을 가리키는 포인터다. 포인터를 vptr, 테이블을 vtable이라고 부른다. vtable은 해당 클래스의 가상 함수의 주소를 담고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1050&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pCSpa/btrFuEo2cAe/Xjy2F7PnBym3Agov2zULT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pCSpa/btrFuEo2cAe/Xjy2F7PnBym3Agov2zULT0/img.png&quot; data-alt=&quot;출처: C++ Primer Plus 6th 741p&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pCSpa/btrFuEo2cAe/Xjy2F7PnBym3Agov2zULT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpCSpa%2FbtrFuEo2cAe%2FXjy2F7PnBym3Agov2zULT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;523&quot; height=&quot;549&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1050&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: C++ Primer Plus 6th 741p&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;virtual function을 쓴다는 것은 다음의 side effect를 일으킨다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;각 오브젝트의 크기가 vptr 등으로 인해 증가한다.&lt;/li&gt;
&lt;li&gt;각 클래스마다 컴파일러가 vtable을 만든다.&lt;/li&gt;
&lt;li&gt;virtual function을 호출할 때마다 vtable을 조회하는 추가적인 절차가 생긴다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Redefinition Hides Methods&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음 상속 관계를 보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655880536073&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Dwelling
{
public:
	virtual void showperks(int a) const;
...
};

class Hovel : Dwelling
{
public:
	virtual void showperks() const; // 재정의했는데, argument가 다르다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이러면 showperks는 int를 받는 버전과 받지 않는 버전 이렇게 2개가 생기는 것일까? 그렇지 않다. 위와 같은 코드는 warning을 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655880599480&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Warning: Hovel::showperks(void) hides Dwelling::showperks(int)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상속 관계에서 재정의는 함수 오버로딩을 추가하는 형태로 이뤄지지 않는다. 아예 이전 내용을 모두 가리고 그 위에 새로 쓰는 방식이다.&lt;/li&gt;
&lt;li&gt;이로 인해 재정의를 할 때 지켜야 하는 규칙이 생긴다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;만약 base-class의 메소드를 재정의해야 한다면 함수 시그니쳐를 동일하게 가져가야 한다. base-class -&amp;gt; derived-class로의 변화는 괜찮다.&lt;/li&gt;
&lt;li&gt;만약 메소드가 오버로딩이 있다면 그 오버로딩을 전부 재정의해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Access Control: protected&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본적으로 protected는 private과 비슷하다. protected인 멤버는 바깥에서 접근할 수 없다. 하지만 상속관계에서는 public과 같다. derived-class가 base-class의 protected 멤버에 접근할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Abstract Base Classes (ABC)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 클래스가 ABC가 되려면 적어도 한 개이상의 pure virtual function이 있어야 한다. pure virtual function은 다음처럼 생겼다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655880955051&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;virtual void Move(int nx, int ny) = 0;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pure virtual function은 사실상 시그니쳐만 있는 함수 껍데기이므로 해당 함수를 가진 클래스는 인스턴스화될 수 없다.&lt;/li&gt;
&lt;li&gt;ABC를 통해 클래스의 인터페이스를 정해줄 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Class Design Review&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Copy Constructor는 다음 상황에서 호출된다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;같은 클래스인 다른 오브젝트로부터 새로운 오브젝트를 생성할 때&lt;/li&gt;
&lt;li&gt;오브젝트가 pass by value로 함수로 넘겨질 때&lt;/li&gt;
&lt;li&gt;함수가 오브젝트를 value로 리턴할 때&lt;/li&gt;
&lt;li&gt;컴파일러가 임시 오브젝트를 생성할 때&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;References&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++ Primer Plus 6th, Chapter 13&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Unreal, C++</category>
      <category>abstract</category>
      <category>C++</category>
      <category>CPP</category>
      <category>virtual</category>
      <author>_rien</author>
      <guid isPermaLink="true">https://ri3n.tistory.com/102</guid>
      <comments>https://ri3n.tistory.com/102#entry102comment</comments>
      <pubDate>Wed, 22 Jun 2022 16:02:52 +0900</pubDate>
    </item>
    <item>
      <title>[C++ Prime Plus] 12. Classes and Dynamic Memory Allocation</title>
      <link>https://ri3n.tistory.com/101</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Dynamic Memory and Classes&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++은 Memory allocation을 runtime에 정하는 방식이다.&lt;/li&gt;
&lt;li&gt;C++은 Dynamic Memory allocation을 위해 new와 delete 연산자를 사용한다.&lt;/li&gt;
&lt;li&gt;이로 인해, 우리는 새로운 문제를 풀어야만 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Special Member Functions&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++은 다음 멤버 함수를 자동으로 만들어낸다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;만약 생성자가 없으면, default constructor를 만들어낸다.&lt;/li&gt;
&lt;li&gt;따로 정의하지 않았다면, default destructor를 만들어낸다.&lt;/li&gt;
&lt;li&gt;따로 정의하지 않았다면, copy constructor를 만들어낸다.&lt;/li&gt;
&lt;li&gt;따로 정의하지 않았다면, = operator를 만들어낸다.&lt;/li&gt;
&lt;li&gt;따로 정의하지 않았다면, &amp;amp; operator를 만들어낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;좀더 정확히 말하자면, default constructor나 default destructor는 없으면 무조건 만들어내지만, copy, =, &amp;amp;는 사용했다면 컴파일러가 만들어내는 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Copy Constructors&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;copy constructor는 다른 오브젝트를 복사해서 객체를 생성할 때 사용된다. 문법은 다음과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655800299524&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ClassName(const ClassName &amp;amp;);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Default Copy constructor는 static이 아닌 모든 member를 복사하는 형태로 만들어진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;When an Assignment Operator Is Used and What It Does&lt;/h4&gt;
&lt;pre id=&quot;code_1655800970708&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Dummy d1;
Dummy d2 = d1; // copy constructor invoked, possibly = operator too.
Dummy d3;
d3 = d1; // assignment operator invoked&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(visual c++에서는 두번째 줄에서 복사 생성자만 호출되었다.)&lt;/li&gt;
&lt;li&gt;여기서 유의할 점은 =을 쓴다고 무조건 = operator가 호출되는 게 아니라는 것이다. 생성하는 상황이면 copy constructor가 호출된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Static Class Member Functions&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;member function에 static을 붙일 수 있다. 이렇게 되면 특정 오브젝트와 멤버 함수가 연관성이 사라지게 되는데, 그러면서&amp;nbsp;&lt;b&gt;두가지 변화&lt;/b&gt;가 생긴다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;some_object.static_method()형태가 아니라 class_name::static_method() 형태로 호출된다.&lt;/li&gt;
&lt;li&gt;static member variable만 가져올 수 있다. (특정 오브젝트와 연관성이 없으므로 생각해보면 당연하긴 함)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Things to Remember When Using new in Constructors&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성자에서 new를 썼다면 소멸자에서는 꼭 delete를 써서 짝을 맞춰주자.&lt;/li&gt;
&lt;li&gt;new - delete, new[] - delete[] 의 짝을 맞춰주자.&lt;/li&gt;
&lt;li&gt;생성자 오버로딩이 되어 있다면 모든 생성자는 new를 비슷한 방식으로 사용해줘야 한다. 그래야 하나있는 소멸자와 짝이 맞춰진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;NULL or 0 or nullptr?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변수를 기본값으로 초기화할 때 보통 0으로 설정한다. 이 때, 타입에 따라서 초기화를 할 때, remind를 위해 다음과 같이 하곤 했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655801373871&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;char c = '\0'; // rather than char c = 0&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 때, '\0'은 0과 같으므로 0이라고 써도 되지만 '\0'이라고 써서 c가 char 타입이라는 걸 상기시켜줬다. nullptr도 pointer형에 똑같은 역할을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655801526809&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int* a = 0; // C style
int* a = NULL; // C++98 style
int* a = nullptr; // C++11 style, and Best one&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;a가 pointer형임을 상기시킴과 동시에 디폴트값으로 초기화할 수 있는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Observations About Returning Objects&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 어떤 함수가 매개변수로 레퍼런스를 받았고 이를 그대로 반환해도 되는 상황이라면 레퍼런스 타입으로 리턴하는 게 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655801695325&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Vector&amp;amp; Max(const Vector&amp;amp; v1, const Vector&amp;amp; v2)
{
	return v1.magval() &amp;gt;= v2.magval ? v1 : v2;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;References&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++ Primer Plus 6th, Chapter 12&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Unreal, C++</category>
      <category>C++</category>
      <category>CPP</category>
      <author>_rien</author>
      <guid isPermaLink="true">https://ri3n.tistory.com/101</guid>
      <comments>https://ri3n.tistory.com/101#entry101comment</comments>
      <pubDate>Tue, 21 Jun 2022 17:55:56 +0900</pubDate>
    </item>
    <item>
      <title>[C++ Prime Plus] 11. Working with Classes</title>
      <link>https://ri3n.tistory.com/100</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Operator Overloading&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Operator Overloading은 C++ Polymophism 중 하나다.&lt;/li&gt;
&lt;li&gt;Operator Overloading을 통해 사용자에게 실제로 어떻게 이런 연산이 이뤄지는지 감출 수 있다. 추상화는 OOP의 목표 중 하나다.&lt;/li&gt;
&lt;li&gt;Operator Overloading을 정의하는 문법은 다음과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655787516825&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;operator op(argument-list)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 때, op는 새로운 symbol을 만들어낼 수 없다. 즉, operator@ 같은 건 만들 수 없다.&lt;/li&gt;
&lt;li&gt;아래 예시를 보자. 컴파일러는 기본적으로 operator 왼쪽에 있는 오브젝트에서 정의됐다고 예상한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655787620089&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;district2 = sid + sara;
district2 = sid.operator+(sara); // same&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Introducing Friends&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++은 private을 통해 data hiding을 구현했다. 이 때, friend 키워드는 외부에서 private에 접근할 수 있도록 허용한다. friend 키워드는 세 종류에 대해 걸 수 있다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Friend functions&lt;/li&gt;
&lt;li&gt;Friend classes&lt;/li&gt;
&lt;li&gt;Friend member functions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Friend는 15 챕터에서 좀더 자세히 다룬다. 여기서는 operator overloading에서 쓰이는 friend만 살펴본다.&lt;/li&gt;
&lt;li&gt;먼저 어떤 경우에 friend 키워드가 필요한지 알아보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655788105905&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A = B * 2.75; // == B.operator*(2.75);
A = 2.75 * B; // ???&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 예시에서 첫줄의 연산이 된다면 둘째줄의 연산도 될 것 같지만 꼭 그렇지가 않다. &lt;b&gt;호출 함수가 다르기 때문이다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;1 줄은 B.operator*(2.75)를, 2줄은 operator(2.75, B)를 호출한다. 연산자에 대한 정의는 연산자 왼쪽에 있는 오브젝트의 member function이거나 non-member function이어야 한다. 이 때, argument는 2개다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655788558827&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Time operator*(double m, const Time&amp;amp; t)
{
	Time result;
	result.totalMinutes = t.totalMinutes * m; // totalMinutes가 private 변수라면??
    
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제는 실제로 구현할 때 나타난다. 이 operator overloading은 non-member function이기 때문에 Time 클래스의 totalMinutes에 접근할 수 없기 때문이다. 이를 해결하기 위해 friend 키워드를 쓸 수 있다. friend는 특정 함수, 클래스, 메소드에게 member function과 같은 정도의 access right를 부여한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655788705462&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 아래 한 줄을 Time class에 추가한다. 
friend Time operator*(double m, const Time&amp;amp; t);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;A Common Kind of Friend: Overloading the &amp;lt;&amp;lt; operator&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;operator overloading에서 friend 키워드를 빈번하게 사용하게 되는 지점이 &amp;lt;&amp;lt; operator다. 이 오버로딩은 주로 ostream 클래스와 연동된다. 가장 단순한 형태는 다음과 같을 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655788912985&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void operator&amp;lt;&amp;lt; (ostream&amp;amp; os, const Time&amp;amp; t)
{
	os &amp;lt;&amp;lt; t.hours &amp;lt;&amp;lt; &quot; hours, &quot; &amp;lt;&amp;lt; t.minutes &amp;lt;&amp;lt; &quot; minutes&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;당연히 위 함수가 잘 돌아가려면 해당 오버로딩 함수가 time의 friend여야 할 것이다.&lt;/li&gt;
&lt;li&gt;사실, 위 구현은 큰 문제가 하나 있다. 아래 코드가 작동하지 않는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655789055268&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cout &amp;lt;&amp;lt; &quot;Trip Time: &quot; &amp;lt;&amp;lt; trip &amp;lt;&amp;lt; &quot; (Tuesday)\n&quot;; // can't do&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이유는 우리가 쓴 오버로딩 함수는 리턴값이 없기 때문이다. 위 코드가 돌아가려면 다시 ostream의 레퍼런스를 리턴해줘야 한다. (lvalue 레퍼런스이기 때문에 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655789146075&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ostream&amp;amp; operator&amp;lt;&amp;lt; (ostream&amp;amp; os, Time t)
{
	...
    return os;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Overloaded Operators: Member Versus Nonmember Functions&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연산자 오버로딩을 member function으로 구현할지, non-member function으로 구현해야 할지는 프로그래머의 선택이다. 특정 연산자는 member function으로만 구현할 수 있지만 다른 경우엔 상관없다.&lt;/li&gt;
&lt;li&gt;주의해야 할 점은 &lt;b&gt;둘 다 정의하면 안된다&lt;/b&gt;는 것이다. 그러면 컴파일러는 모호하다고 판단해서 에러를 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Automatic Conversions and Type Casts for Classes&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Class와 기본자료형 사이 형변환을 구현할 수 있다.&lt;/li&gt;
&lt;li&gt;놀랍게도! 우리는 이미 이 형변환을 구현했었다. 바로 &lt;b&gt;생성자&lt;/b&gt;다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655789884152&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Stonewt
{
public:
	Stonewt(double lbs);
    ...
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Stonewt 클래스는 double 하나를 받는 생성자가 있다. 이는 생성자일 뿐만 아니라 double에서 Stonewt로의 형변환이기도 하다. 즉, 아래 코드가 작동된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655790006512&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Stonewt myCat;
myCat = 19.6&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;근데 위 코드는 myCat이 실수 자료형처럼 보이게 하는 등 혼동을 줄 수 있다. 그래서 이런 implicit conversion을 막아둘 수 있는데, 방법은 해당 생성자에 explicit 키워드를 맨 앞에 추가하는 것이다. explicit 키워드는 explicit conversion만 허용해주는 역할이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655790098236&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;explicit Stonewt(double lb);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이제 클래스&amp;rarr;기본자료형을 보자. 문법은 아래와 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655790190053&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;operator typeName();&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;conversion function은 다음 3가지 특성을 가진다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;메소드여야 한다.&lt;/li&gt;
&lt;li&gt;리턴 타입이 없어야 한다.&lt;/li&gt;
&lt;li&gt;매개 변수가 없어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;위 특성을 지키는 구현부는 다음과 같을 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655790313097&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Stonewt::operator double() const
{
	return pounds;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 conversion function은 explicit이 아니니까 아래 코드는 동작할까?&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655790420358&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Stonewt poppins;
...
double p_wt = poppins;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정답은 '그럴 수도 있고 아닐 수도 있다'이다. 만약 Stonewt가 int conversion도 구현했다면 컴파일러는 에러를 준다. int로 형변화해야할지, double로 형변환해야할지 애매하기 때문이다. 그런데 만약 double conversion만 있다면 에러가 없다. 더이상 모호하지 않기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;References&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++ Prime Plus 6th, Chapter 11&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Unreal, C++</category>
      <category>C++</category>
      <category>conversion function</category>
      <category>CPP</category>
      <category>explicit</category>
      <category>operator overloading</category>
      <author>_rien</author>
      <guid isPermaLink="true">https://ri3n.tistory.com/100</guid>
      <comments>https://ri3n.tistory.com/100#entry100comment</comments>
      <pubDate>Tue, 21 Jun 2022 14:50:03 +0900</pubDate>
    </item>
    <item>
      <title>[C++ Primer Plus] 9. Memory Models and Namespaces</title>
      <link>https://ri3n.tistory.com/99</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Seperate Compilation&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1챕터에서 말했듯, C++는 각 파일마다 컴파일을 따로(.c &amp;rarr; .o)할 수 있다. 이런 방식덕분에 소스코드 하나를 수정하면 그거만 컴파일하고 링킹을 해서 실행파일을 만들 수 있다. Unix나 Linux의 make가 이런 관리를 도와준다.&lt;/li&gt;
&lt;li&gt;한 소스 코드의 내용을 나눠야 한다고 생각해보자. 예를 들어, 클래스 구현부와 main()을 나누는 것이다. 이런 상황에서는 main에서 클래스 구현부를 알 필요가 있다. 이런 상황을 해결해주는 것이 #include 기능이다. 즉, 총 3개의 파일로 나눠질 것이다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클래스의 정의와 메소드의 프로토타입을 담은 .hpp 파일&lt;/li&gt;
&lt;li&gt;클래스 구현 코드를 담은 .cpp 파일&lt;/li&gt;
&lt;li&gt;클래스 메소드를 사용하는 .cpp 파일&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;헤더 파일에는 주로 다음의 내용을 담는다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Function protypes&lt;/li&gt;
&lt;li&gt;Symbolic constants defined using #define or const&lt;/li&gt;
&lt;li&gt;Structure declarations&lt;/li&gt;
&lt;li&gt;Class declarations&lt;/li&gt;
&lt;li&gt;Template declarations&lt;/li&gt;
&lt;li&gt;Inline functions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;함수의 구체적인 알고리즘 자체를 쓰는 것은 Template과 Inline 함수다. &lt;b&gt;왜냐하면 Template과 Inline 함수는 실제로 Instantiate 되지 않기 때문이다.&lt;/b&gt; (=STL이 헤더에 다 정의된 이유)반대 이유로 non-templae function은 헤더에 정의하면 안된다. 그러면 다른 파일에서 같은 헤더를 include하면 같은 name을 가진 함수가 2개 instantiate 되기 때문이다.&lt;/li&gt;
&lt;li&gt;#include에서 &amp;lt;&amp;gt;와 &quot;&quot;는 다른 의미를 가진다. &amp;lt;&amp;gt;가 더 좁은 의미를 가진다고 이해하면 된다. &amp;lt;&amp;gt;는 컴파일러에게 이 헤더 파일이 표준 헤더 파일이니까 파일 시스템에서 찾아보라고 알려주는 것이다. 반대로 &quot;&quot;로 감싼 헤더는 컴파일러가 먼저 현재 디렉토리나 소스코드 디렉토리를 찾아보게 한다. 이래도 없으면 표준 헤더 파일을 찾아본다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&amp;lt;&amp;gt; : 표준 헤더 폴더&lt;/li&gt;
&lt;li&gt;&quot;&quot; : 현재 폴더 + 소스코드 폴더&amp;nbsp;&amp;rarr; 표준 헤더 폴더&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Multiple Library Linking&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++ 컴파일러는 적절한 상황에 쓰여야 하는 함수를 빠르게 찾기 위해 name decoration 또는 name mangling이라는 작업을 수행한다.&lt;/li&gt;
&lt;li&gt;예를 들어, long MyFunctionFoo(int, float)를 ?MyFunctionFoo@@YAYH로 만든다. 문제는 이런 name decoration 방식이 컴파일러마다 다르다는 것이다.&lt;/li&gt;
&lt;li&gt;즉 컴파일된 라이브러리를 사용할 때는 같은 컴파일러로 컴파일을 했는지 확인해야 한다. 다른 컴파일러로 컴파일됐다면 name decoration 규칙이 다르기 때문에 제대로 링킹을 할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Storage Duration, Scope, and Linkage&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++은 (11 기준) 메모리를 네가지로 나누어 관리한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Automatic storage duration&lt;br /&gt;자신이 정의된 블록이 시작될 때 생겨나서 블록이 끝나면 사라진다.&lt;/li&gt;
&lt;li&gt;Static storage duration&lt;br /&gt;프로그램이 시작될 때 생겨나서 프로그램이 끝날 때 사라진다.&lt;/li&gt;
&lt;li&gt;Thread storage duration (C++11)&lt;br /&gt;쓰레드가 시작될 때 생겨나서 쓰레드가 끝날 때 사라진다.&lt;/li&gt;
&lt;li&gt;Dynamic storage duration&lt;br /&gt;new 연산자로 생겨나고 delete 연산자로 사라진다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Scope and Linkage&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Scope는 name이 file 안에서 어디까지 보이는 지(visible)를 결정한다.&lt;/li&gt;
&lt;li&gt;Linkage는 name이 각 단위(unit)에서 어떻게 공유될 수 있는지 결정한다. 예를 들어, external linkage는 file 사이에서 공유되고, internal linkage는 같은 파일, 다른 함수 사이에서 공유된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Automatic Storage Duration&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수의 매개변수나 함수 안에서 정의된 변수는 기본적으로 automatic storage duration을 갖는다. 또한 local scope에 no linkage다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655618653154&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main()
{ // teledeli allocated
	int teledeli = 5;
    { // websight allocated
    	cout &amp;lt;&amp;lt; &quot;Hello\n&quot;;
        int websight = -2;
        cout &amp;lt;&amp;lt; &quot;websight&quot; &amp;lt;&amp;lt; ' ' &amp;lt;&amp;lt; teledeli &amp;lt;&amp;lt; endl;
    } // websight expires
} // teledeli expires&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램은 automatic storage duration을 가진 변수들을 관리하기 위해 stack이라는 메모리 관리 기법을 사용한다. 그래서 애초에 이 메모리 공간 자체를 stack이라고 부른다. stack의 크기는 각기 다르지만 컴파일러에 플래그를 줘서 조정할 수 있다.&lt;/li&gt;
&lt;li&gt;stack은 LIFO(Last In First Out) 특성을 가지고 있다. 즉, 마지막으로 정의된 변수가 가장 먼저 나간다. 보통 함수가 시작되면 그 매개변수를 스택에 가장 먼저 쌓고 top을 올린다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Static Duration Variables&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++의 Static Duration Variables는 3가지 linkage를 갖는다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;external linkage (across files)&lt;/li&gt;
&lt;li&gt;internal linkage (across functions in a single file)&lt;/li&gt;
&lt;li&gt;no linkage (accessible to just one function or block)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;컴파일러는 모든 static 함수를 정해진 메모리 블록에 함께 적재해놓는다. 그리고 따로 static 함수를 초기화하지 않으면 0으로 초기화해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655619198098&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int global = 1000; // static duration, external linkage
static int one_file = 50; // static duration, internal linkage

int main()
{
	...
}

void func1(int n)
{
	static int count = 0; // static duration, no linkage
    int llama = 0;
}

void func2(int q)
{
	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;static variable은 세가지 초기화 방법을 가지고 있다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;zero initialization&lt;/li&gt;
&lt;li&gt;constant-expression initialization&lt;/li&gt;
&lt;li&gt;dynamic initialization&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655619354177&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;cmath&amp;gt;
int x; // zero
int y = 5; // constant-expression
long z = 13 * 13; // constant-expression
const double pi = 4.0 * atan(1.0); // dynamic&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Static Duration, External Linkage&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;external linkage를 가진 변수를 external variables 또는 global variables라고 부른다.&lt;/li&gt;
&lt;li&gt;'One Definition Rule'에 따라 정의는 빌드 범위 내에서 단한번 수행되어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655619617191&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double up;
extern int blem; // 다른 곳에서 blem을 정의했을 것이다. 그 값을 갖다 쓴다.
extern char gr = 'z'; // gr은 이 파일에서 정의된다. 다른 곳에서는 정의하면 안된다.&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참고로 no linkage를 가진 static duration variable은 재귀 함수 등에서 쓰면 굳이 매개변수로 넘겨줄 필요가 없어서 편하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Specifiers and Qualifiers&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;storage class specifier와 cv-qualifiers가 있다. 이를 구분해서 알아둬야 한다.&lt;/li&gt;
&lt;li&gt;storage class specifiers
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;register&lt;/li&gt;
&lt;li&gt;static&lt;/li&gt;
&lt;li&gt;extern&lt;/li&gt;
&lt;li&gt;thread_local&lt;/li&gt;
&lt;li&gt;mutable&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;cv-qualifiers
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;const&lt;/li&gt;
&lt;li&gt;volatile&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;cv는 const와 volatile을 의미한다. const는 한번 정의된 이후로는 해당 프로그램에서 값이 수정되는 일이 없다는 것을 의미한다. volatile은 완전 반대다. 해당 프로그램에서 수정하지 않았음에도 불구하고 값이 바뀔 수 있음을 의미한다. 예를 들어, 하드웨어에 의해 값이 바뀔 수 있다.&lt;/li&gt;
&lt;li&gt;mutable은 const struct임에도 불구하고 값을 수정할 수 있는 멤버를 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655620022748&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct data
{
	char name[30];
    mutable int accesses;
};

const data veep = {&quot;Hyun&quot;, 0};
veep.accesses++; // Allowed&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;const는 storage class specifier를 조금 바꿀 수 있다. 예를 들어, static duration을 가진 변수를 const로 선언하면 기본적으로 static을 붙인 것과 같다. 즉, external linkage를 가지지 않는다. external linkage를 가지려면 extern specifier를 앞에 또 써주면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655620108117&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const int fingers = 10; // internal linkage, same as static const int fingers
extern const int fingers = 10; // external linkage&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Functions and Linkage&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수도 Linkage를 가진다. 하지만 변수에 비해 폭이 아주 좁다.&lt;/li&gt;
&lt;li&gt;기본적으로 함수는 external linkage를 가진다. 그래서 extern specifier를 함수 앞에 붙여줄 수 있지만 붙이나 마나 똑같다.&lt;/li&gt;
&lt;li&gt;함수에 internal linkage를 주려면 static specifier를 쓰면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655620289874&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static int private(double x); // interal linkage&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Language Linking&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Linker는 각 함수마다 symbolic name이 필요하다. 문제는 C와 C++이 symbolic name을 만드는 방법이 다르다는 것이다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;spiff(int)는 C에서 _spiff로, C++에서 _spiff_i로 바뀐다. C++에는 함수 오버로딩이 있기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;때문에 특정 라이브러리가 C로 만들어졌으면 제대로 linking 할 수가 없다. 그래서 look-up convention을 써줄 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655620585437&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extern &quot;C&quot; void spiff(void); // C 방식의 이름을 찾으라고 링커에게 지시
extern void spoff(int); // C++ 방식의 이름을 찾으라고 링커에게 지시
extern &quot;C++&quot; void spiff(int); // C++ 방식의 이름을 찾으라고 링커에게 지시&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Storage Schemes and Dynamic Allocation&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;new 연산자에서 초기화를 함께 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655620795894&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int *pi = new int (6);
double * pd = new double (99.99);

// C++11 way
struct where {double x; double y; double z;};
where * one = new where {2.5, 5.3, 7.2};
int * ar = new int [4] {2,4,6,7};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Placement new 를 사용해서 특정 메모리 공간을 할당받을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655621009534&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;new&amp;gt; # placement new는 new 헤더를 include해야 한다.
struct chaff
{
	char dross[20];
    int slag;
};

char buffer1[50];
char buffer2[500];

int main()
{
	chaff *p1, *p2;
    int *p3, *p4;
    
    p1 = new chaff; // heap에서 할당받음
    p3 = new int[20]; // heap에서 할당받음
    
    p2 = new (buffer1) chaff; // buffer1의 공간을 할당받음. new (sizeof(chaff), buffer1) 호출
    p4 = new (buffer2) int[20]; // buffer2의 공간을 할당받음
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Namespaces&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램이 커지고 복잡해지면서 같은 이름이 충돌하는 namespace problem이 부상했다. 이를 해결해주는 것이 Namespace다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655622864892&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace Jack {
	double pail;
    void fetch();
    int pal;
    struct Well { ... };
};

namespace Jill {
	double bucket(double n) { ... }
    double fetch;
    int pal; // Jack의 pal 변수와 이름이 겹치지만 namespace가 다르므로 괜찮다.
    struct Hill { ... };
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;using Declarations and using Directives&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;using declaration은 특정 네임스페이스의 특정 이름을 쉽게 쓸 수 있게 해주고, using directive는 특정 네임스페이스 전체를 바로 접근할 수 있게 해준다.&lt;/li&gt;
&lt;li&gt;using declaration 예제&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655623027759&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main()
{
	using Jill::fetch; // Jill::fetch를 local namespace로 옮긴다.
    double fetch; // ERROR! 이름이 겹친다.
}

///

using Jill::fetch; // Jill::fetch를 global namespace로 옮긴다.

int main()
{
	cin &amp;gt;&amp;gt; fetch;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;using directive 예제&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655623246828&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using namespace Jack;

int main()
{
	pal = 4; // 알아서 Jack::pal을 의미함
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 예시만 보면 using declaration을 전부 적용한 것이 using directive가 아니냐고 할 수 있는데, using directive는 그보다는 scope resolution(::)을 전체적으로 적용한 것에 가깝다.&lt;/li&gt;
&lt;li&gt;즉,&amp;nbsp; 해당 namespace를 global namespace로 끌고 내려와주는 것이다. 그만큼 읽는 입장에서 혼동이 온다.&lt;/li&gt;
&lt;li&gt;namespace가 여러개 겹치면 길어질 수 있는데, 이를 간단하게 만들기 위해 alias를 설정할 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655623413984&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace MEF = myth::elements::fire;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;References&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++ Prime Plus 6th, Chapter 9&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Coding/Unreal, C++</category>
      <category>C++</category>
      <category>C++ Prime Plus</category>
      <category>C++ 기초 플러스</category>
      <category>linking</category>
      <category>Namespace</category>
      <category>Using</category>
      <author>_rien</author>
      <guid isPermaLink="true">https://ri3n.tistory.com/99</guid>
      <comments>https://ri3n.tistory.com/99#entry99comment</comments>
      <pubDate>Sun, 19 Jun 2022 16:24:48 +0900</pubDate>
    </item>
  </channel>
</rss>