C/CPP 문법
평소 궁금하던 C 및 CPP 에서 사용되는 여러 개념을 다루고 그 필요를 정리해보자
1. <> , “ ” 의 차이
c 나 cpp를 하다 보면 소스코드 가장 위에 #include
#include <iostream>
: 해당 문법은 iostream 이라는 헤더 파일의 존재를 컴파일러가 이미 알고 있을 때 사용한다.
#include "Async/Async.h"
: 해당 문법은 현 directory를 기반으로 사용자가 경로를 지정해 헤더 파일을 읽어 올 수 있도록 한다.
2. Namespace
namespace는 중복되는 함수 이름을 허용하기 위해서 도입된 개념이다. namesapce를 정의하고 그 안에 함수를 작성하면 함수의 이름이 충돌 되는 것을 방지할 수 있다.
1 |
|
3. Struct
structure는 C 언어에서 가장 중요한 개념이라고 생각한다. 자세히 다루자면 복잡하지만 일단 좀 더 안전한 코딩을 하거나 디버깅 시에 유리하다 정도로 알고 있는다면 좋을 것 같다.
1 |
|
위 예시와 같은 상황에 여러 비슷한 변수가 있을 때 실수를 사전에 방지하고 예외가 없도록 할 수 있다.
4. Call by value
c 쪽 언어의 제일 큰 특성은 call by value 라는 점이다. 이러한 특성 때문에 pointer 라는 개념이 등장했다고 봐도 무방하다.
1 |
|
Pointer 와 Refernce에 차이
둘 다 call by value의 특성을 극복하기 위해서 사용한다. pointer는 주소를, reference는 별칭을 의미한다고 생각하면 편할 것 같다. 그렇기 때문에 pointer는 반드시 선언과 동시에 정의할 필요는 없지만, refernce 무조건 초기화 해주어야 한다.
5. OverLoading
객체지향언어 큰 특징 중 하나인 overloading 이다. 기능이 같은 함수에 여러가지 input값을 넣거나 그에 따라 return type을 다르게 하고 싶은 경우는 매우 자주 발생하는 상황이다. C와 다르게 CPP는 overloading이라는 기능으로 이를 지원한다
1 |
|
function signature(name,param 수 타입) 이 다르다면 overloading 조건이 성립된다. 하지만 return 타입은 이에 포함되지 않는 것이 중요한 점이다.
직관적으로 생각한다면 호출 시 max(1, 2) 하면 무슨 리턴 타입의 함수를 호출한 것인지 알 수 없다. float aa = max(int a, int b) 하면 되지 않나 라는 의문을 갖을 수 있지만 묵시적 형변환 이라는 특성 때문에 안된다.
1 |
|
6. OverRiding
OverLoading이 OverRiding 과 차별점은 상속(inherit)에 있다. 같은 기능을 여러가지 객체에서 사용할 경우 유리하다. 불필요한 반복 코드를 줄여주는 유용한 기능이다
1 |
|
Parent p;
이와 같은 선언 방법은 메모리가 Stack 영역에 할당된다. 할당 된 메모리는 함수 호출 완료 시 자동으로 해제된다.
Parent *p = new Parent();
이와 같은 선언 방법은 메모리가 Heap 영역에 할당된다. 사용자가 delete를 사용하여 임의로 해제하기 전까지 유지된다.
따라서 CPP에서는 2번째 방법으로 메모리 누수가 없도록 객체를 직접 관리하는 것을 추천한다.
7. Shallow & Deep Copy
-
shallow copy : 새로운 객체를 생성하고 원래 객체의 참조(reference)를 복사합니다. 이것은 원래 객체와 새로운 객체가 같은 객체를 참조하게 됩니다. 따라서, 새로운 객체가 변경되면 원래 객체도 변경됩니다. 이러한 이유로 Shallow copy는 객체를 복사하는 것처럼 보이지만, 실제로는 같은 객체를 참조합니다.
기본적으로 .clone( ) 함수나 .copy( ) 등 함수를 사용하지 않는 이상 shallow copy(Mat P = C , Mat P(C))가 진행된다.
1
2
3
4
5
6//shallow cv::Mat image2(image); image2 = image; //deep image.copyTo(image2); cv::Mat image2= image.clone();
= 연산자
가 좀 헷갈리는데= 연산자
는 상황에 따라 다른 것 같다. 자료형이 pointer 일 때는 shallow copy이고 int등일 때는 deep copy 가 될 것이다. 같은 함수에 인자를 & 타입으로 받는 경우도 있는데 그 부분은 내부 구현 부분이 중요한 것 같다.(주로 shallow copy를 진행하도록 구현하는 듯) -
deep copy : 새로운 객체를 생성하고 원래 객체의 모든 내용을 복사한다. 이것은 원래 객체와 새로운 객체가 서로 다른 객체를 참조하게 된다. 따라서, 새로운 객체가 변경되어도 원래 객체는 변경되지 않는다. 이러한 이유로 Deep copy는 객체를 완전히 복사한다.
8. Template
- tempalte은 코드의 재활용성을 높이기 위해 사용한다. 자료형에 따라 코드를 overloading 할 필요 없이 template을 사용하면 된다.
1 |
|
9. .
, ::
, ->
연산자
.
은 멤버 접근 연산자이다. 이 연산자는 객체나 포인터가 가리키는 structure이나 class의 멤버 변수나 멤버 함수에 접근할 때 사용된다. 예를 들어,obj.member
는obj
객체의member
멤버 변수에 접근하는 것이라 볼 수 있다.::
은 범위 지정 연산자이다. 이 연산자는 class , namespace 등에서 이름을 참조할 때 사용된다. 예를 들어,std::cout
는std
namespace에 있는cout
을 참조한다.->
은 포인터 멤버 접근 연산자이다. 이 연산자는 객체나 structure의 포인터가 가리키는 멤버 변수나 멤버 함수에 접근할 때 사용된다. 예를 들어,ptr->member
는ptr
포인터가 가리키는 객체member
멤버 변수에 접근하는 것이다. 또한->
는(*p)
로 표현 또한 가능하다.
10. ++iter , iter++
둘 중 더욱 적은 메모리를 사용하는 것은 무엇일까? ++iter이 새로운 값 지정 없이 자기 자신을 리턴하기 때문에 더욱 이득이다.
11. dynamic library, static library(dll,ilb)
static library 사용 시 exe 파일이 커진다 .ilb(정적) 라이브러리 사용 시 해당 함수 내용을 통으로 복사한다. 하지만 각 exe 파일이 중복되는 라이브러리를 포함하고 있는 것은 합리적이지 않다. 이러한 단점을 극복하고자 .dll을 사용한다. Dynamic library는 런타임 시 메모리에 라이브러리를 올려 메모리 사용을 줄이면서 링크가 가능하다. 그렇다면 메모리를 아낄 수 있는 dynamic library를 사용하는 것이 유리하다고 생각할 수 있지만 static library를 사용 시 exe 파일만 제공한다면 라이브러리가 없는 환경에서도 돌아간다(그리고 조금 더 빠르다).
궁금한 점이 있다면 남겨주세요! 함께 고민해 보겠습니다.