-
-
Save Curookie/bb3019a8134f09adeff77a6015da0841 to your computer and use it in GitHub Desktop.
C++ 기초
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
C++은 .h를 기본 제공 헤더파일일 경우 생략한다. | |
.h를 생략하고 앞에 c를 붙이면 기본 c언어에 대응하는 c++의 헤더파일 이름이 된다. | |
ex) #include <stdio.h> no! -> #include <cstdio> yes! | |
(이게 overloading 되어 있어서 더 친절하기 때문에 사용하기에 좋다) | |
bool 1byte 자료형 추가 | |
cout = Console Output의 약자 | |
cin = Console Input의 약자 | |
endl = End Line의 약자 | |
Overloading 매개변수의 자료형 or 개수 (+const함수의 여부) 가 다른 같은 이름의 함수나 클래스. | |
반환형은 상관없다. ex) void A(int n) {} no! Overloading int A(int n) {} | |
함수의 오버로딩은 상속의 관계에서도 구현될 수 있다. | |
Default 매개변수 값을 지정할 수 있다. ex) void A(int n=1, int m=2) {} | |
A(3);일 경우 두번째 변수 m을 입력해 주지 않았기에 n=3, m=2가 된다. | |
(+ 함수의 선언 부분에만 Default를 표시해도 된다.) | |
(+ Default값은 매개변수의 일부분에만 적용할 수 있으나 반드시 오른쪽부터 Default값을 체우는 형태로만 가능) | |
C++ 도 C같이 함수 선언을 먼저 해야한다. main 함수 마지막 | |
다만, 지역함수 안에서 변수 선언은 자유롭다. | |
C++은 데이터의 입력과 출력에 별도의 포맷 지정이 필요 없다. ex) C# 1.24f 1.24d 필요없음 %d %s 필요없음 | |
inline 함수는 속도향상의 목적에서 사용하는데 C언어의 매크로함수와 비교된다. | |
사용법은 함수 반환형 앞에 inline만 붙이면 된다. | |
매크로함수의 정의는 복잡하나 인라인 함수의 정의는 일반함수와 같다. 하지만 | |
자료형에 의존적이지 않는 면은 매크로함수의 장점이라 할 수 있다. | |
매크로함수는 전처리기, 인라인함수는 컴파일러에 의해서 처리된다. | |
namespace는 이름을 붙여놓은 공간, 특정 영역에 이름을 붙여주기 위한 문법적 요소이다. | |
사용법은 거의 최상위에 namespace NAME { ~~ } 이런식으로 사용한다. | |
:: 는 범위지정 연산자(scope resolution operator)라 하며, 이름공간을 지정할 때 사용하는 연산자이다. 또한 전역변수에 접근할 때도 사용된다. | |
ex) int a=30; { int a=20; a+=3; ::a+=3; } // 이때 뒤의 ::a는 전역변수로 접근 | |
중첩이 가능하고 Name::Name2::A 이런식으로 접근이 가능하다. | |
using으로 이름공간의 명시를 할 수 있다. | |
ex) using namespace 네임스페이스; or using 네임스페이스::함수; or using 네임스페이스::변수; 가능 이 안에서는 명시한 namespace이후 절을 붙일 필요가 없다. | |
namespace ABC=AAA::BBB::CCC; 이런식으로 네임스페이스에 별칭을 줄 수 있다. | |
참조자(reference) : 변수에 별칭을 하나 붙여주는 것이다. pointer와 다름 | |
참조자는 &를 붙이면 선언가능 | |
참조자는 반드시 선언과 동시에 변수를 참조해야한다. | |
int num1 = 10; | |
int &num2 = num1; //이런식으로 | |
참조자는 선언되고나면 변수와 다름없다. 주소값도 같음. 변수 이름이 두개 생긴다고 생각하면 됨. | |
참조자는 여러개 선언가능하며 참조자를 대상으로 참조자 선언도 가능 | |
포인터변수와 더블포인터 역시 참조자 선언할 수 있다. | |
참조자는 함수와 더블어 자주 사용되며, 포인터를 활용한 것이 아닌 참조자를 사용한 CallByReference를 가능하게 해준다. | |
const로 참조자를 통한 값 변경 제한 선언을 할 수 있다. | |
ex) 함수에서 void HappyFunc(const int &ref) { } 이런식으로 선언하면 | |
함수내에서 참조자 ref를 이용한 값의 변경은 하지 않겠다는 의미로 사용할 수 있다. | |
반환값에 참조자 형태로 반환할 수 있다. | |
malloc의 문제점 ( -> new써야하는 이유) | |
할당할 대상의 정보를 무조건 바이트 크기의 단위로 전달해야한다. | |
반환형이 void형 포인터이기 때문에 적당한 형 변환을 해야한다. | |
new 한만큼 delete해줘야 한다. | |
C++ 클래스 | |
함수가 포함된 struct + 접근제어자 | |
쓰이는 클래스가 있다고 먼저 선언해주는 것을 맨 위에 적을 필요도 있다. | |
ex) class AClass; | |
접근제어자 3가지 | |
1. private (Default - 함수 내) | |
2. public (모든 곳에서) | |
3. protected (상속관계까지 접근가능) | |
접근범위 : private < protected < public | |
C++ 클래스의 파일분할 | |
Car.h 클래스의 선언을 담는다. | |
Car.cpp 클래스의 정의(멤버함수의 정의)를 담는다. | |
멤버변수는 클래스 안에서 선언과 초기화를 동시에 할 수 없다. | |
(생성자로 초기화하면됨) | |
public으로 선언된 멤버변수는 구조체변수 초기화하듯 ex) AClass AObj = { 2, 1 }; 초기화가 가능하다. | |
Set, Get 함수를 엑세스 함수(access function)라고 한다. 멤버변수를 private로 선언하면서 | |
클래스 외부에서의 멤버변수 접근을 목적으로 정의되는 함수들이다. | |
const 함수 | |
ex) int GetX() const; | |
이 함수 내에서는 멤버변수에 저장된 값을 변경하지 않겠다. | |
const 함수 내에서는 const가 아닌 함수의 호출이 제한된다. | |
오버로딩 조건에 const 함수도 들어간다. | |
const 객체 | |
ex) const AClass A(20); | |
이 객체의 데이터 변경을 허용하지 않겠다. 라는 의미 = 멤버 함수의 호출만 허용하는 것. = public이여도 호출 불가능 const함수만 호출가능하다. | |
캡슐화는 복잡도를 해결하고 객체의 활용도를 높이기 위해 사용한다. | |
객체지향 전문가를 판단할 수 있는 지표 | |
생성자 | |
반환형 없고, 클래스와 이름 동일 | |
객체 생성 시 딱 한번 호출된다. | |
생성자도 오버로딩 가능, 매개변수에 디폴트 값 적용가능 | |
1. 3가지 형태가능하나 | |
Class sc();는 안된다. | |
그 이유는 함수내에서 함수원형선언시에만 사용해야지, 객체생성문으로 착각하지 않을 수 있기 때문에 함수원형 선언용으로만 사용한다. | |
Class sc; | |
Class * ptr = new Class; | |
Class * ptr = new Class(); | |
2. 멤버변수로 선언된 객체(자식 클래스)의 생성자 호출 시에는 멤버 이니셜라이져[:]를 이용해 멤버 초기화를 할 수 있다. ( :멤버변수(초기화); ) | |
ex) class A { private: B cb; B cb2; public: A(const int &x1, const int &y1):cb(x1, y1), cb2(x1, y1) { ~ } }; | |
이걸 사용하다보면 생성자의 몸체부분이 그냥 비는 경우가 종종 발생한다. | |
생성자는 정의하지 않아도 자동으로 디폴트 생성자로 생성된다. | |
3. 이니셜라이저를 이용하면 선언과 동시에 초기화가 이뤄지기 때문에 그냥 멤버변수 뿐아니라 const 멤버변수, 참조자도 초기화가 가능하다. | |
- 이니셜라이저를 사용하면 약간의 성능향상을 기대할 수 있다. | |
private 생성자는 public 함수를 이용해서 만들 수 있다. | |
소멸자 | |
클래스 이름앞에 ~ 붙은 애 | |
반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다. | |
매개변수는 void형으로 선언되어야 하기 때문에 오버로딩도, 디폴트 값 선언불가능 | |
생성자에서 할당한 리소스의 소멸에 사용된다. | |
객체 배열 | |
ex) Class arr[10]; | |
객체 배열은 생성자에 인자를 전달하지 못해 기본 생성자만 가능하다. | |
객체 포인터 배열도 가능하다. | |
ex) Class * arr[10]; | |
클래스의 구조는 똑같으나 둘의 저장 방법은 확연히 다르다. | |
This 포인터 | |
객체자신을 가리키는 포인터 | |
맴버변수와 매개변수를 구분하는 용도로 많이 사용된다. | |
this로 멤버변수 접근가능 ex) this->멤버변수 | |
Self-Reference (This 포인터 확장) | |
*this는 객체자체를 반환한다는 의미로서 Self-Reference라고도 하며, &참조자로 받을 수 있다. | |
이를 이용하면 ex) ref.Afunc().Bfunc().Afunc(); 이런식의 문장이 가능해진다. | |
복사 생성자 (Copy Constructor) | |
일반 생성자와 호출되는 시점이 다른 참조자 형태로 생성하는 생성자. ex) AClass A(10,100); AClass B=A; | |
int num=20 | |
-> int num(20) 이런식으로 묵시적 형변환 된다. 이걸 막으려면 선언 시 explicit 키워드를 붙이면 복사생성자로만 생성가능 ex) AClass B=A (X) AClass B(A) (O) | |
복사 생성자를 지정하지 않으면, 디폴트 복사 생성자가 자동으로 삽입된다. | |
얇은 복사, 포인터 그대로 복사하여 두 복사본이 같은 대상을 가르키는 현상을 유발한다. | |
깊은 복사, 멤버뿐아니라, 포인터로 참조하는 대상까지 깊게 복사하는 것을 말한다. | |
복사 생성자가 호출되는 3가지 시점 | |
case1. 기존에 생성된 객체를 이용해서 새로운 객체를 초기화하는 경우 | |
case2. Call-by-Value 방식의 함수호출 과정에서 객체를 인자로 전달하는 경우 | |
case3. 객체를 반환하되, 참조형으로 반환하지 않은 경우 | |
3가지 모두 객체를 새로 생성해야 하고, 생성 동시에 동일한 자료형으로 초기화해야 한다. | |
함수 안에 복사생성자 호출이 있을 경우 매개변수 파괴 후, 반환 객체의 리턴값을 제공한다. | |
임시변수 | |
선언없이 변수처럼 쓰이는 주로 상수들이 그렇게 쓰인다. | |
ex) const &a = 3; 일때 3은 임시변수 | |
임시객체 | |
임시객체는 참조 값을 반환한다. 따라서 AClass(100).hamsu() 이런식으로 사용할 수 있다. | |
특징 | |
- 다음 행으로 넘어가면 바로 소멸되어 버린다. | |
- 참조자에 참조되는 임시객체는 바로 소멸되지 않는다. | |
ex) AClass(10); 선언없이 이렇게 임시객체를 생성할 수 있다. | |
AClass는 임시객체의 참조값이 반환된다. | |
ex) AClass B = AClass(객체); 이런식으로 임시객체를 사용해서 추가 객체 생성없이 효율성을 높일 수 있다. | |
- return 시, 함수의 호출 시(인자)에도 임시객체를 사용할 수 있다. | |
클래스의 friend 친구 | |
friend class 클래스명; private public 어디든 선언가능하다. | |
친구 비유 A속마음 B -> B속마음 A -> 모두 접근가능 | |
A클래스가 B클래스 대상으로 friend선언을 하면, B클래스는 A클래스의 private맴버에 직접 접근 가능, | |
단 A가 B의 private맴버에 직접 접근하려면 B가 friend선언 해줘야함 | |
ex) | |
class Boy | |
{ | |
private: | |
int height; | |
freind class Girl; //Girl 클래스를 friend로 선언함 | |
... | |
}; | |
friend 선언을 남발하면 정보은닉을 깨고 더 큰 문제를 야기할 수 있으니 소극적으로 사용하고, 연산자 오버로딩 할 때 약으로 쓰일 수 있다. | |
friend 선언을 사용해서 전역함수와 맴버함수 둘다 적용할 수 있다. | |
ex) friend void ShowAClass(const AClass&); | |
그리고 전역변수 선언일 시, 함수의원형 선언이 포함되어 있다. | |
void ShowAClass(const AClass&); 가 포함 | |
static 선언 | |
멤버변수, 멤버함수에 static선언을 할 수 있는 C++ | |
전역변수에 선언된 static의 의미 | |
-> 선언된 파일 내에서만 참조를 허용하겠다는 의미 | |
ex)static int c; //이 함수를 실행 시 한번만 참조되고 자동으로 0으로 초기화 된다. | |
함수 내에 선언된 static의 의미 | |
-> 한번만 초기화되고, 지역변수와 달리 함수를 빠져나가도 소멸되지 않는다. | |
전역변수는 어디서나 접근이 가능하기 때문에 문제가 생길 수 있는데 이 때 static멤버로 선언하면 이런 문제를 해결할 수 있다. | |
static 멤버변수(클래스 변수) | |
- 일반적인 멤버변수와 달리 클래스당 한 개씩만 생성되는 변수 | |
- 객체를 생성하건 생성하지 않건, 메모리 공간에 딱 하나만 할당이 되어서 공유되는 변수이다. | |
- 생성 및 소멸 시점도 전역변수와 동일하다. | |
- 클래스 외부에서 초기화시켜야 한다. 아니면 생성자에서 생성될 때마다 초기화 되기 때문에 불가능. | |
- 단, const static 변수는 선언과 동시에 초기화 가능 ex) const static int RUSSIA = 1707540; | |
- 어디서든 접근가능하며, 객체 내에 존재하지 않는다. | |
static 멤버함수 | |
- 선언된 클래스의 모든 객체가 공유한다. | |
- public으로 선언이 되면, 클래스의 이름을 이용해서 호출이 가능하다. | |
- 객체의 맴버로 존재하는 것이 아니다. | |
- static으로 선언된 멤버변수가 아니라면 접근 불가하다. = static은 static끼리 논다. | |
mutable 키워드 | |
- const 함수 내에서의 값의 변경을 예외적으로 허용한다. | |
- 사용을 최대한 자제해야하는 키워드 -> const의 선언을 의미 없게 만들어버리기 때문이다. | |
상속(Inheritance) | |
- 모든 멤버를 물려받는 것. :로 상속한다. | |
ex) class A : public B { ~ }; | |
- 부모 클래스의 멤버를 초기화할 의무를 지니기 때문에, 상속한 클래스(부모 클래스)의 생성자를 호출하는 형태로 초기화하는 것이 좋다. | |
ex) B(int a, char * b) : A(a) { } | |
- 상속한 private 멤버에 직접 접근은 불가능하다. | |
- 용어정리 | |
상속한 상속을 받은 | |
상위 클래스 하위 클래스 | |
기초(base)클래스 유도(derived)클래스 | |
슈퍼(super)클래스 서브(sub)클래스 | |
부모 클래스 자식 클래스 | |
- 유도 클래스의 객체생성 과정에서 기초 클래스의 생성자는 100%호출되고 먼저 호출된다, 명시하지 않으면 void생성자로 생성된다. | |
- 유도 클래스의 객체가 소멸될 때에는, 유도 클래스의 소멸자가 실행되고 난 다음에 기초 클래스의 소멸자가 실행된다. | |
- 스택의 생성된 객체의 소멸순서는 생성순서와 반대이다. | |
- 생성자에서 동적 할당한 메모리 공간은 소멸자에서 해제한다.의 규칙을 지켜야한다. | |
- 3가지 상속이 존재한다. public, protected, private 상속 | |
- 해당 접근제어자 보다 넓은 접근제어자를 해당상속 접근제어자로 바꾼다. (다중상속이 아닌경우 잘 사용하지 않는다. 그냥 다 public) | |
ex) class B : private A 상속일 시 public, protected접근제어자를 모두 private로 바꾸어 외부에서 접근할 수 없다. | |
- 상속의 조건 IS a 관계가 성립되어야한다. (일종의 ~이다.) ex) 무선 전화기는 일종의 전화기 이다. 무선 전화기 is a 전화기. | |
- 상속의 조건 HAS a 관계, ~을 소유하다. 관계 ex) 경찰은 총을 소유하다. 경찰 has a 총 | |
- HAS a 일 때 상속은 아니지만 객체를 참조하는 방식도 있다. 왜? 상속으로 묶인 두 개의 클래스는 강한 연관성을 띄기 때문에 NULL인 경우를 표현하기 위해 | |
객체 포인터 변수 | |
- A형 포인터 변수는 A 객체 또는 A를 직접 혹은 간접적으로 상속하는 모든 객체를 가리킬 수 있다. (객체의 주소 값을 저장할 수 있다.) | |
ex) Person * ptr = new Person(); 뿐 아니라 ptr = new Student(); 유도 클래스 Student도 가리킬 수 있음 | |
- 그 대신 이 경우 포인터 형에 해당하는 클래스에 정의된 멤버에만 접근이 가능하다. | |
ex) 위에 경우 Person * ptr = Student() 이므로 ptr는 Student클래스의 멤버에는 접근이 불가능하고 오직 Person의 멤버에만 접근가능, | |
그러나 멤버앞에 virtual을 Student() 멤버기준으로 접근됨. | |
- 참조자도 이런 참조가능하다. | |
함수 오버라이딩 (function overriding) | |
- 오버라이딩을 할 경우, 기초 클래스의 함수가 가려진다. 유도 클래스의 오버라이딩한 함수만 보이게 된다. | |
- 단, 클래스 이름을 명시함으로 인해 기초 클래스의 오버라이딩 된 함수를 호출할 수 있다. ::(스코프 연산자) 활용해서 | |
- ex) object.AClass::Overriding(); 이런식으로도 호출할 수도 있다. | |
- 완전히 똑같은 함수를 오버라이딩 해야할 경우도 생긴다. 왜? 기초 클래스의 함수는 기초 클래스의 함수를 불러오기 때문이다. | |
가상 함수 (Virtual Function) | |
- 아무리 오버라이딩이 되어있어도 포인터 함수의 자료형에 따라 호출되는 함수의 종류가 달라진다. 이것을 방지하기 위해 함수 앞에 virtual 키워드를 붙이면 | |
함수 호출시, 포인터의 자료형을 기반으로 호출대상을 정하지 않고, 포인터 변수가 실제로 가리키는 객체를 참조하여 호출의 대상을 결정한다. | |
추상 클래스(Abstract Class) | |
- 클래스 중에는 객체생성 목적으로 정의되지 않는 클래스로, 1개 이상의 순수 가상 함수가 있으면 추상클래스가 된다. | |
- 추상 클래스로 객체생성을 막아 개발자의 실수를 피하고 코드의 안정성을 얻을 수 있다. | |
순수 가상 함수 (Pure Virtual Function) | |
- 추상클래스를 만들기 위해, 함수앞에 virtual 키워드를 붙이고 const = 0;형태로 정의된 함수다. | |
- 함수의 몸체가 정의되지 않은 함수, '= 0' 은 컴파일러에게 명시적으로 알려주는 의미 | |
ex) virtual int AFunc() const = 0; | |
다형성(Polymorphism) | |
- 클래스를 상속받은 경우 오버라이딩을 할 수 있는데 그럼 같은 함수지만 다른 방식으로 결과를 낼 수 있다. | |
- 이런 구조를 다형성이라고 한다. | |
- 결국 상속 후 오버라이딩의 형태 = 다형성 | |
- 모습은 같은데 형태는 다르다. = 동질이상 | |
- 문장은 같은데 결과가 다른 경우를 뜻한다. | |
ex) virtual 선언된 함수 SimpleFunc() | |
First * ptr = new First(); | |
ptr->SimpleFunc(); // 동일하지만 다른결과 | |
delete ptr; | |
ptr= new Second(); | |
ptr->SimpleFunc(); // 동일하지만 다른결과 | |
delete ptr; | |
가상 소멸자(Virtual Destructor) | |
- 기본 소멸자 역시 상속된다. | |
- 소멸자는 자식 클래스의 소멸자가 먼저 불려지고 나서 부모 클래스의 소멸자가 불려진다. | |
- 소멸자는 오버라이딩 된다. 기초 클래스 포인터로 유도 클래스를 호출했을 경우 유도클래스의 소멸자가 불러지는게 아니라 | |
기초 클래스의 소멸자가 불러져 유도 클래스의 소멸이 안되는 경우를 방지하기위해 사용. | |
- 기초 클래스 소멸자만 virtual로 선언하면 상속하는 유도클래스의 소멸자가 대신 호출된다. | |
ex) virtual ~AClass() { ~ } | |
멤버함수는 객체안에 존재한다는 것은 개념적으로 생각하는 것이고 | |
사실 상 함수 포인터로 고정 메모리안에 존재하는 함수를 가져다 쓰는 형태로 컴파일 된다. | |
객체는 virtual 함수 테이블을 참조하는 형태로 구성되어 있어 아주 조금의 속도의 저하가 있을 수 있다. | |
다중 상속(Multiple Inheritance) | |
둘 이상의 클래스를 상속하는 것. | |
득보단 실이 많기 때문에 가급적 사용하지 않아야한다. | |
알아두기만 하자. | |
문제되는 것은 멤버이름이 같을 때 모호성이 있어 어떤멤버를 불러와야할지 모른다. (해결법 '::' 로 알려줘야함) | |
가상 상속(Virtual Inheritance) | |
상속을 virtual로 하면 생성자가 한번만 호출된다. 다중 상속의 해결책이 될 수 있다. | |
연산자 오버로딩(Operator Overloading) | |
- 객체도 기본자료형 변수처럼 덧셈, 뺄셈 등 사칙연산이 가능하게 하려고 설계한 것이다. | |
- Point pos1.operater+(pos2) 이런식의 표현을 pos1+ pos2 로 가능하게 한다. | |
- 사용법은 operator를 사용해서 함수를 설계하면 되며 뒤에 원하는 연산자를 붙이면 된다. | |
- const함수로 선언이 가능하다. | |
- 두 가지 방법이 있는데, 멤버함수에 의한 오버로딩과 전역함수에 의한 오버로딩이 있다. | |
- pos1.operator+(pos2) 멤버함수, operator+(pos1, pos2) 전역함수, 멤버함수로 오버로딩된 것이 더 우선순위가 높고 많이 그렇게 쓰인다. | |
- 오버로딩이 불가능한 연산자의 종류로는 . .* :: ?: sizeof typeid static_cast dynamic_cast const_cast reinterpret_cast | |
- 본래 의도를 벗어난 연산자 오버로딩은 좋지 않다. | |
- 기본 기능을 변경하는 + 인데 *한다든지 이런건 허용되지 않는다. | |
- 단항연산자는 참조값을 리턴하는 식으로 표현하면 ++(++abc) 이런식으로 표현할 수 있다. 멤버변수, 전역변수 표현방식이 다르다. | |
ex)멤버 자신& operator단항연산자() { 계산 return *this; } | |
ex)전역 자신& operator단항연산자(자신 &참조형변수) { 계산 return 참조형변수; } | |
- 전위증가와 후위증가를 구별하는 방법은 int키워드로 구분하기로 약속 | |
ex)Point& operator++() //전위 | |
{ | |
xpos += 1; | |
ypos += 1; | |
return *this; | |
} | |
const Point operator++(int) //후위 | |
{ | |
const Point retobj(xpos, ypos); | |
xpos += 1; | |
ypos += 1; | |
return retobj; | |
} | |
friend Point& operator--(Point &ref); //전위 전역 | |
friend const Point operator--(Point &ref, int); // 후위 전역 | |
- 전역함수를 기반으로 연산자를 오버로딩 해야 하는 경우도 있다. | |
- iostream 헤더파일 안의 std 이름공간의 ostream 클래스 안의 cout객체 | |
- iostream 헤더파일 안의 std 이름공간의 istream 클래스 안의 cin객체 | |
- <<로 객체를 보여주는 연산자 오버로딩은 전역함수를 기반으로 해야한다. | |
ex) friend ostream& operator<< (ostream&, const Point&); | |
}; | |
ostream& operator<< (ostream& os, const Point& pos) | |
{ | |
os << '[' << pos.xpos << ", " << pos.ypos << ']' << endl; | |
return os; | |
} | |
대입 연산자 | |
- 복사 생성자와 매우 흡사하다. | |
- 정의하지 않으면 디폴트 대입 연산자가 삽입된다. | |
- 디폴트 대입 연산자는 멤버 대 멤버의 앏은복사를 진행한다. | |
- 연산자 내에서 동적할당을 한다면, 그리고 깊은복사가 필요하다면 직접 정의해야 한다. | |
- 초기화가 진행된 객체이여야 한다. | |
- 디폴트 대입 연산자의 경우 메모리 누수가 발생하지않도록 깊은복사에 앞서 메모리 해체 과정을 거치고 깊은 복사를 진행하도록 정의한다. | |
- 상속 시 유도클래스의 대입연산자 오버로딩이 덮어버려 기초클래스의 대입 연산자 오버로딩을 섀도잉시킨다. | |
- ex) Police pman1(5, 2); | |
Police pman2(0, 4); | |
Police pman3 = pman1; // 복사 생성자 | |
pman3.Shot(); | |
pman3.PutHandcuff(); | |
pman3 = pman2; // 대입 연산자 | |
pman3.Shot(); | |
pman3.PutHandcuff(); | |
- 대입연산자가 깊은복사를 시킬려면 ref 참조자로 참조해서 this리턴 꼭 값을 넣기전에 delete[] 시켜줘야한다. | |
배열 인덱스 연산자 오버로딩 | |
- 맴버함수기반으로만 오버로딩해야하는 연산자들이 존재한다. ex) 배열의 인덱스 연산자 [ ] | |
- 배열의 인덱스 연산자 [] 오버로딩 경우에는 배열의 유일성을 보장하기 위해 private로 복사와 대입을 완전히 막아주는게 좋다. | |
- const참조자만 입력해도 오버로딩이 된다. | |
- 객체 배열을 저장하는 대입연산자 방식에는 객체자체를 저장하는 방식과 객체의 포인터를 저장하는 방식으로 나뉘는데 | |
얇은복사 깊은복사를 신경쓰지 않아도 되는 후자가 더 많이 쓰인다. | |
- const BoundCheckIntArray& ref 와 같이 매개변수 참조자를 const선언해 버렸을 때 ref[idx], ref.operator[] (idx); | |
에러가 날 수 있는데, 이 때 const선언을 추가 해주면 된다. 참조값이 아닌 배열요소의 값을 단순히 반환하는 형태로 정의해야한다. | |
ex) int& operator[] (int idx) | |
{ | |
if (idx < 0 || idx >= arrlen) | |
{ | |
cout << "Array index out of bound exception" << endl; | |
exit(1); | |
} | |
return arr[idx]; | |
} | |
int operator[] (int idx) const | |
{ | |
if (idx < 0 || idx >= arrlen) | |
{ | |
cout << "Array index out of bound exception" << endl; | |
exit(1); | |
} | |
return arr[idx]; | |
} | |
new, delete 연산자 오버로딩 | |
- new,delete 연산자 오버로딩에서는 [메모리 공간의 할당,소멸]만 오버로딩 할 수 있다. 모든 호출과 리턴의 주체는 컴파일러이며, 선언 형태가 정의되어 있다. | |
※ void형 delete를 지원하지 않는 컴파일러일 경우 delete []((char*)adr); 이런식으로 형변환시키면 된다. | |
※ size_t는 unsigned int 형이고 size는 byte단위 즉, 1byte이다. new, delete 대입 함수는 static으로 선언된 함수로서 객체가 만들어지기전에 실행된다. | |
ex) | |
void * operator new (size_t size) | |
{ | |
cout << "operator new : " << size << endl; | |
void * adr = new char[size]; | |
return adr; | |
} | |
void operator delete (void * adr) | |
{ | |
cout << "operator delete () " << endl; | |
delete[] adr; | |
} | |
new(delete) 연산자의 역할 [키워드가 아니다!!] | |
- 메모리 공간의 할당(소멸) | |
- 생성자(소멸자)의 호출 | |
- 할당(소멸)하고자 하는 자료형에 맞게 반환된 주소 값의 형 변환 | |
포인트 연산자 오버로딩 | |
- 대표적으로 *, -> 연산자가 있다. | |
- private 선언된 변수를 수정가능하게 만들어 버릴수 있다. | |
ex) | |
Number * operator->() | |
{ | |
return this; | |
} | |
Number& operator*() | |
{ | |
return *this; | |
} | |
스마트 포인터(Smart Pointer) | |
- 자기스스로 하는 일이 존재하는 포인터로서, 포인터의 역할을 하는 객체를 의미한다. | |
- 포인터 연산자 오버로딩을 사용한다. | |
- 개인적으로 구현해서 사용하는것은 드물고 전문가들이 조금씩 다듬어가는 라이브러리를 사용하는 편이다. | |
- delete 연산이 자동으로 되는 것 같은 기능. | |
ex) | |
class SmartPtr | |
{ | |
private: | |
Point * posptr; | |
public: | |
SmartPtr(Point * ptr) : posptr(ptr) { } | |
Point& operator*() const | |
{ | |
return *posptr; | |
} | |
Point* operator->() const | |
{ | |
return posptr; | |
} | |
~SmartPtr() | |
{ | |
delete posptr; | |
} | |
}; | |
펑터(Functor) | |
- 객체를 함수처럼 사용할 수 있는 기법이다. | |
- ()연산자 오버로딩을 사용해서 진행하는 것이다. | |
ex) | |
class Adder | |
{ | |
public: | |
int operator()(const int& n1, const int& n2) | |
{ | |
return n1 + n2; | |
} | |
double operator()(const double& e1, const double& e2) | |
{ | |
return e1 + e2; | |
} | |
Point operator()(const Point& pos1, const Point& pos2) | |
{ | |
return pos1 + pos2; | |
} | |
}; | |
int main(void) | |
{ | |
Adder adder; | |
cout << adder(1, 3) << endl; | |
cout << adder(1.5, 3.7) << endl; | |
cout << adder(Point(3, 4), Point(7, 9)); | |
return 0; | |
} | |
- 함수나 객체의 동적방식에 유연함을 제공할 때 유용하게 사용된다. ex) 버블정렬 | |
ex) void SortData(const SortRule& functor) | |
{ | |
for (int i = 0; i < (idx - 1); i++) | |
{ | |
for (int j = 0; j < (idx - 1) - i; j++) | |
{ | |
if (functor(arr[j], arr[j + 1])) | |
{ | |
int temp = arr[j]; | |
arr[j] = arr[j + 1]; | |
arr[j + 1] = temp; | |
} | |
} | |
} | |
} | |
임시객체로의 자동 형 변환과 형 변환 연산자 오버로딩(Conversion Operator) | |
- A형 객체가 와야 할 위치에 B형 데이터(또는 객체)가 왔을 경우, B형 데이터를 인자로 전달받는 A형 클래스의 생성자 호출을 통해서 A형 임시객체를 반환한다. | |
- ex) | |
Number num; | |
num=30; -> num=Number(30); 클래스안에있는 생성자로 임시객체 생성 -> num.operator=(Number(30)); 대입연산자 호출 | |
- 형변환 연산자의 오버로딩 : 반환타입이 없는데 반환한다. | |
ex) | |
operator int() // 형변환 연산자의 오버로딩, 반환타입이 없는데 반환한다. | |
{ | |
return num; | |
} | |
Number num1; | |
num1 = 30; | |
Number num2 = num1 + 20; | |
C++의 표준 라이브러리 | |
1. String클래스 | |
- #include <string>으로 불러올 수 있다. | |
- string 변수선언, 객체간의 덧샘이 가능, +=가능, ==로 비교가능, 문자열 입,출력가능 | |
Template 템플릿 | |
- 모형자 라는 뜻이 담겨있다. 이미 만들어진 틀같은 존재이다. | |
- 함수템플릿은 함수를 만드는 도구가 된다. | |
- 함수템플릿은 다양한 자료형의 함수를 만들어 낼 수 있다. | |
- 형태는 함수의 매개변수와 리턴타입의 자료형을 가상의 자료형으로 설정한다. | |
- 정의는 아래와 같다. | |
ex) Template <typename T> | |
T Add(T num1, T num2) | |
{ | |
return num1+num2; | |
} | |
- <class T> 도 되고 T대신 다른 문자를 사용해도 된다. | |
- 컴파일 할때 만들어지며, 한번 만들어진 함수는 그저 호출만 하게된다. | |
- 인자의 자료형을 참고하여 호출될 함수를 정한다. | |
- 정의된 것을 함수템플릿, 생성되는것들을 템플릿함수(=생성된함수)라고한다. | |
- 매개변수, 리턴함수 조차도 기본 자료형으로 선언될 수 있다. 형변환에도 템플릿 자료형으로 쓸 수 있다. | |
- c++에서 형변환은 데이터에 소괄호를 묶는형태로 표현해도 된다. ex) int num = (int)3.14; -> int num = int(3.14); 완전히 같다. | |
- 특수한 케이스 예외처리는 함수 템플릿의 특수화라고 한다. | |
ex) template<> | |
char* Max(char* a, char *b) | |
{ | |
cout<<"char* Max<char*>(char* a, char *b)"<<endl; | |
return strlen(a) > strlen(b) ? a : b ; | |
} | |
- 클래스 템플릿(Class Template)은 클래스를 템플릿화하는 것 | |
- 클래스 템플릿 기반의 객체생성에는 반드시 자료형 정보를 명시하도록 되어있다. | |
- 클래스 템플릿도 멤버변수를 클래스 외부에 정의하는 것이 가능하다. 외부에 정의하는 과정에서 template<typename T>를 빼먹으면 에러남. | |
- 배열클래스도 템플릿화 할 수 있다. | |
- Class<Point<int>> orra(50); 이런형태로 템플릿화할수 있다. | |
- Class<Point<int>*> orra(50); 이런형태도 가능. | |
- 특정 템플릿 클래스의 객체를 인자로 받는 인자로 받는 일반함수로 표현할 수 있으며, friend 선언도 가능하다. | |
ex) friend Point<int> operator+(const Point<int>, const Point<int>&); | |
friend ostream& operator<<(ostream& os, const Point<int>& pos); | |
}; | |
Point<int> operator+(const Point<int>& pos1, const Point<int>& pos2) | |
{ | |
return Point<int>(pos1.xpos + pos2.xpos + pos1.ypos + pos2.ypos); | |
} | |
ostream& operator<<(ostream& os, const Point<int>& pos) | |
{ | |
os << '[' << pos.xpos << ", " << pos.ypos << ']' << endl; | |
return os; | |
} | |
- 클래스 템플릿 특수화는 템플릿 클래스를 오버로딩하는 것이라 생각하면 쉽다. template <> 형태로 시작한다. | |
ex)template <> | |
class SimpleDataWrapper <Point<int>> | |
{ | |
private: | |
Point<int> mdata; | |
public: | |
SimpleDataWrapper(int x, int y) : mdata(x, y) { } | |
void ShowDataInfo(void) | |
{ | |
mdata.ShowPosition(); | |
} | |
}; | |
- 클래스 템플릿의 부분 특수화는 결정되지 않은 자료형이 두개 이상일때 부분만 특수화 하는 경우를 말한다. | |
ex) template <typename T1> | |
class MySimple<T1, double> | |
{ | |
public: | |
void WhoAreYou() | |
{ | |
cout << "size of T1: " << sizeof(T1) << endl; | |
cout << "size of double: " << sizeof(double) << endl; | |
cout << "<T1, double>" << endl; | |
} | |
}; | |
- 전체 특수화와 부분 특수화가 중복될 경우 우선순위는 전체 특수화가 더 높다. | |
- 정해지지 않은 자료형을 의미하는 문자 ex) T 를 "템플릿 매개변수"라고 한다. 템플릿 매개변수에 전달되는 자료형 정보를 "템플릿 인자"라고 한다. | |
- *템플릿 매개변수 선언에는 변수를 쓸 수도 있다. 이것의 이점은 서로 다른 형의 클래스가 생성되게 할 수 있다는 점이다. | |
ex) template <typename T, int len> | |
class SimpleArray | |
{ | |
private: | |
T arr[len]; | |
- 템플릿 매개변수에도 디폴트 값을 지정할 수 있다. | |
ex) template <typename T=int, int len=7> | |
class SimpleArray | |
{ | |
private: | |
(... 중략) | |
SimpleArray<> arr; // 디폴트 생성 | |
- static 지역변수 선언 시 함수별로 템플릿 함수별로 변수가 관리가능하다. | |
ex)template <typename T> | |
void ShowStaticValue(void) | |
{ | |
static T num = 0; | |
num += 1; | |
cout << num << " "; | |
} | |
이 때 int형 함수로 건드리는 것과 long형으로 건드리는게 각각 다르다. | |
- 클래스 템플릿 안에 static 멤버변수 선언 시, 객체간 공유가능한 변수가 되기 때문에 템플릿 클래스 별로 static 멤버변수를 유지하게 된다. | |
ex) template <typename T> | |
class SimpleStaticMem | |
{ | |
private: | |
static T mem; | |
public: | |
void AddMem(int num) { mem += num; } | |
void ShowMem() { cout << mem << endl; } | |
}; | |
template <typename T> // 이걸 써줘야 한다. | |
T SimpleStaticMem<T>::mem = 0; // 이 때 이 문장은 템플릿 기반의 static 멤버 초기화 문장이다. | |
- 템플릿 관련 정의에서는 template<typename T>, template<>같은 선언을 둬, 템플릿의 일부또는 전부를 정의하고 있음을 알려야한다. | |
- 특정 자료형만 초기화 시 template<>로 특수화를 해야한다. | |
ex) template <> | |
long SimpleStaticMem<long>::mem=5; | |
예외처리(Exception Handling) | |
- 컴파일 시 발생하는 문법적인 에러(Syntax error)가 아닌, 실행도중에 발생하는 문제사항(Runtime error)을 예외(Exception)라고 한다. | |
- if문을 사용해서 예외처리를 할 수 있다. | |
- try catch throw 3개의 키워드 throw에 의해 던져진 '예외 데이터'는, '예외 데이터'를 감싸는 try 블록에 의해서 감지가 되어 이어서 | |
등장하는 catch 블록에 의해 처리된다. | |
ex) try | |
{ | |
if (num2 == 0) | |
throw num2; | |
cout << "나눗셈의 몫: " << num1 / num2 << endl; | |
cout << "나눗셈의 나머지: " << num1%num2 << endl; | |
} | |
catch (int expn) | |
{ | |
cout << "제수는 " << expn << "이 될 수 없습니다." << endl; | |
cout << "프로그램을 다시 실행하세요." << endl; | |
} | |
\ | |
- throw절에 의해 던져진 예외 데이터의 자료형과 catch블록의 매개변수 자료형은 일치해야한다. | |
- try 블록 내에서 예외가 발생하면, 예외가 발생한 지점 이후의 나머지 try영역은 건너뛴다. | |
- try 블록 내에서 예외가 발생하지 않으면, catch문을 건너뛴다. | |
- 예외가 처리되지 않으면, 예외가 발생한 함수를 호출한 영역으로 예외 데이터가(더불어 예외처리에 대한 책임까지) 전달된다. | |
- 스택 풀기(Stack Unwinding) : 예외가 처리되지 않아서, 함수를 호출한 영역으로 예외 데이터가 전달되는 현상을 가리켜 '스택 풀기'라고 한다. | |
함수가 연결되어 있으면 스택이 풀리면서 호출된 곳으로 간다. | |
- 자료형이 일치하지 않으면 함수를 호출한 영역으로 예외 데이터가 전달된다. | |
- 하나의 try블록에 이어서 등장하는 여러개의 catch문도 가능하다. | |
- 예외를 함수 옆에 명시할 수 있다. | |
ex) int ThrowFunc(int num) throw (int, char) 단, 다른자료형의 예외 데이터가 전달될경우 종료된다. | |
무조건종료되는코드 ex) int SimpleFunc(void) throw () | |
- unexpected 함수는 명시되지 않은 예외가 전달될 경우 발생하는 함수로, 이 함수가 호출되어 프로그램이 종료된다. | |
- 예외발생을 알리는데 사용되는 객체를 가리켜 '예외객체'라 하며, 예외객체의 생성을 위해 정의된 클래스를 '예외클래스'라 한다. | |
- 예외 클래스를 상속의 관계로 묶을 수 있다. | |
- 상속 예외 클래스 일 경우 catch문의 순서가 중요해진다. base 클래스 객체를 맨뒤에 배치해야한다. | |
- new연산자에 의해 발생하는 예외는 bad_alloc이고 .what() 메서드로 원인정보를 알수있다. | |
- bad_cast 예외도 있다. | |
- 예외처리는 최대한 간결할수록 좋다. | |
- 받은 예외를 다시 던지는 형태도 가능하다. catch문 안에 throw; 라고 쓰면 된다. | |
ex) void Divide(int num1, int num2) | |
{ | |
try | |
{ | |
if (num2 == 0) | |
throw 0; | |
cout << "몫: " << num1 / num2 << endl; | |
cout << "나머지: " << num1%num2 << endl; | |
} | |
catch (int expn) | |
{ | |
cout << "first catch" << endl; | |
throw; //예외 다시던지기 | |
} | |
} | |
int main(void) | |
{ | |
try | |
{ | |
Divide(9, 2); | |
Divide(4, 0); | |
} | |
catch (int expn) | |
{ | |
cout << "second catch" << endl; | |
} | |
return 0; | |
} | |
C++ 타입 캐스팅 4가지 | |
1. static_cast 캐스팅 (일반적인 타입 캐스팅) | |
C언어의 타입 캐스팅과 동일하다. | |
지정한 타입으로 무조건 변경하는 것이 아니라 논리적으로 변환 가능한 타입만 변환한다. | |
<>안에는 타입을 지정하고, ()안에는 캐스팅할 대상을 지정한다. | |
cosnt(상수) 속성에 대한 변경은 불가 | |
[Example] | |
double A; | |
int B = static_cast(A); | |
2. const_cast 캐스팅 (상수성 추가 / 제거) | |
변수, 포인터 변수 또는 참조형의 상수성을 추가 / 제거를 위한 캐스팅에만 사용 | |
[Example] | |
const int A = 10; | |
int B = const_cast(A); | |
3. reinterpret_cast 캐스팅 (포인터끼리 타입 캐스팅) | |
포인터 간의 캐스팅 할 때 사용한다.(함수 포인터에도 적용이 가능) | |
캐스팅 전이나 후에 자체가 포인터라는 사실에는 변경이 없어야 한다. | |
어떤 포인터든지 강제적으로 캐스팅이 가능하기 때문에 적절히 사용하면 유용하나, 잘 못 사용할 경우 매우 위험해 진다. | |
[Example] | |
int *pa; | |
char *pb; | |
pa = reinterpret_cast<int *>(123); | |
pb = reinterpret_cast<char *>(pa); | |
4. dynamic_cast 캐스팅 (상속관계에 있을때 타입 캐스팅) | |
dynamic_cast<타입>(대상) | |
상속관계 안에서 포인터나 참조자 타입을 기본클래스에서 파생클래스로의 다운캐스팅과 다중 상속에서 기본 클래스간의 안전한 타입캐스팅에 사용된다. | |
객체가 위치한 메모리의 시작부분을 찾는 용도로도 사용된다. | |
부모 객체를 자식 객체로 다운 캐스팅 하려고 했을 때, 안전하지 않은 경우 NULL을 리턴한다. | |
부모 객체가 자식 객체를 가리키고 있을 때 자식 객체로 다운 캐스팅은 가능하지만, 자식 객체 대신 자신을 가리키고 있을 때에는 다운 캐스팅이 불가능하다. | |
const(상수) 속성에 대한 변경은 불가능하다. | |
[Example] | |
class Home; | |
class Door; // Home 클래스를 상속받은 클래스라고 가정 | |
Home *pObj1 = new Door; | |
Door *pObj2 = dyamic_cast(pObj1); | |
출처: https://memoryfilm.tistory.com/17 [린월의 Memory Film] | |
C++에서 형변환 연산자 | |
- 오래된 C스타일 형 변환 연산자 : (자료형) - "사람 잡는 모기약" 같은 비유 = 너무 강력한 형변환이 아닌 새로운 스타일 | |
ex) 너무 강력해서 발생하는 문제 | |
Car * pcar2 = new Car(120); | |
Truck * ptruck2 = (Truck *)pcar2; // 문제가 바로 보이는 형변환! | |
ptruck2->ShowTruckState(); | |
- 기초 클래스의 포인터 형을 유도 클래스의 포인터 형으로 형 변환하는 것은 일반적인 경우의 형 변환이 아니다. | |
ex) Car * pcar1 = new Truck(80, 200); | |
Truck * ptruck1 = (Truck *)pcar1; // 문제가 없어 보이는 형변환! | |
ptruck1->ShowTruckState(); | |
- 4개의 의도하는 바를 명확히 표시할 수 있는 형 변환 연산자 dynamic_cast, reinterpret_cast, const_cast, static_cast | |
- dynamic_cast는 상속관계에서의 안전한 형 변환을 위해 사용하는 캐스트 연산자로 | |
dynamic_cast<T>(expr) 형태를 띄고있다. 반드시 T에는 객체의 포인터나 참조형이 와야한다. | |
상속관계에 놓여 있는 두 클래스 사이에서 유도 클래스의 포인터 및 참조형 데이터를 | |
기초 클래스의 포인터 및 참조형 데이터로 형 변환하는 경우 사용한다. | |
- 적절하지 않을 시 컴파일 시 에러. | |
- 상속관계에 있는 유도클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터로 형 변환하는 것. | |
ex) //Car * pcar1 = new Truck(80, 200); //컴파일 에러 | |
//Truck * ptruck1 = dynamic_cast<Truck *>(pcar1); | |
//Car * pcar2 = new Car(120); //컴파일 에러 | |
//Truck * ptruck2 = dynamic_cast<Truck *>(pcar2); | |
Truck * ptruck3 = new Truck(70, 150); | |
Car * pcar3 = dynamic_cast<Car *>(ptruck3); | |
return 0; | |
- 단, 특별한 케이스로 static처럼 기초클래스 -> 유도클래스도 가능한데 기초클래스가 Polymorphic 클래스 일 때 가능하다. | |
- 여기서 Polymorphic 클래스란 하나 이상의 가상함수를 지닌 클래스를 뜻한다. | |
ex) class SoSimple | |
{ | |
public: | |
virtual void ShowSimpleInfo() | |
{ | |
cout << "SoSimple Base Class" << endl; | |
} | |
}; | |
class SoComplex : public SoSimple | |
{ | |
public: | |
void ShowSimpleInfo() | |
{ | |
cout << "SoComplex Derived Class" << endl; | |
} | |
}; | |
int main(void) | |
{ | |
SoSimple * simPtr = new SoComplex; | |
SoComplex * comPtr = dynamic_cast<SoComplex*>(simPtr); | |
comPtr->ShowSimpleInfo(); | |
return 0; | |
} | |
- dynamic_cast는 안정적인 형변환을 보장한다. 실행중에 컴파일러가 안정성을 검사하는 바이너리 코드를 생성한다. | |
- 이것이 static과 dynamic의 차이. | |
- bad_cast예외는 dynamic_cast 연산자를 이용한 형 변환과정에서 발생할 수 있는 예외이다. NULL 값이 나올 때 발생. | |
- static_cast는 A타입에서 B타입으로 캐스팅하는 연산자로, dynamic_cast연산자 보다 속도가 빨라 많이 쓴다. | |
static_cast<T>(expr) 형태를 띄고있고, dynamic_cast를 포함하고 기초클래스의 포인터 및 참조형 데이터도 유도클래스의 포인터 및 참조형으로 | |
변환시키는 것도 허용한다. | |
- 기본 자료형 데이터간의 형 변환에도 사용한다. | |
ex) double result = static_cast<double>(20)/3; | |
- const_cast는 const의 성향을 삭제하는 연산자로 사용된다, 형태는 const_cast<T>(expr); 형태를 띈다. | |
- volatile(컴파일러의 최적화 제한)성향의 제거에도 사용될 수 있다. | |
volatile 컴파일러의 변수의 최적화를 제거하는 키워드 | |
int a; | |
a = 0; | |
a = 1; | |
a = 3; | |
이런 식의 코드가 있다고 한다면, a에는 최종적으로 3의 값이 들어가게 되며 이전의 작업인 0과 1은 의미가 없게 된다. | |
따라서 재정의를 하는 경우에는 컴파일러가 알아서 위의 두 작업을 삭제한다. 이를 통해 수행시간의 이득을 가져올 수 있다. | |
하지만, | |
만약 메모리를 참조하여 하드웨어에 명령을 내리는 코드라고 가정하고 a를 메모리 쓰기 변수라고 한다면 | |
a = 0; | |
a = 1; | |
a = 3; | |
이라는 명령어 자체가 하나의 지시 기능을 가지게 될 것, 따라서, a = 0;, a = 1; 의 두 작업이 최적화를 통해 없어진다면 | |
0, 1에 해당하는 작업을 실행하지 못하게 됨. | |
이런 상황을 방지하고자 volatile 키워드가 존재함. | |
volatile int a = 0; | |
a = 0; | |
a = 1; | |
a = 3; | |
이렇게 선언하게 된다면, a = 0;, a = 1;, a = 3; 의 변수 대입 작업을 전부 실행하게 된다. | |
유사하게 하드웨어 메모리에서 변하는 값을 가져와 실행하는 변수들에도 volatile 키워드는 필수! | |
즉, 외부 요인에 의해 변수 값이 변경될 가능성이 있는 변수에는 volatile을 써줘야 한다. | |
- Memory-mapped I/O | |
- 인터럽트 서비스 루틴 | |
- 멀티 쓰레드 환경 | |
등등.. | |
- reinterpret_cast는 포인터를 대상으로 하는, 그리고 포인터와 관련이 있는 모든 유형의 형 변환을 허용한다. | |
- reinterpret_cast<T>(expr); 형태를 취한다. | |
STL(Standard Template Library)을 포함하는 C++ 표준 라이브러리 | |
- 추가적으로 공부하면 좋다. | |
- 자료구조와 그에 관련된 알고리즘을 구현해 놓은 템플릿의 모임 | |
C++은 아니나 기본 문법 | |
typedef 원문 별칭; 으로 원문을 별칭으로 압축할 수 있다. ex) typedef Point * POINT_PTR; | |
typedef struct { | |
구조체 | |
}별칭; | |
enum {A=1, B, C }; 이런식으로 enum 가능! A=1 B=2 ..의미 | |
const int num=10; | |
변수 num을 상수화 | |
const int * ptr1=&val1; | |
포인터 ptr1를 사용해서 val1의 값을 변경할 수 없다. | |
int * const ptr2=&val2; | |
포인터 ptr2가 상수화 됨 | |
const int * const ptr3=&val3; | |
포인터 ptr3가 상수화되고 ptr3를 사용해서 val3의 값을 변경할 수 없다. | |
데이터 - 전역변수가 저장되는 영역 | |
스택 - 지역변수 및 매개변수가 저장되는 영역 | |
힙 - malloc 함수호출에 의해 프로그램이 실행되는 과정에서 동적으로 할당이 이뤄지는 영역 | |
malloc 함수호출에 의해 할당된 메모리 공간은 free 함수호출을 통해서 소멸하지 않으면 해제되지 않는다. | |
callbyvalue | |
cllbyreference | |
매개변수는 함수가 호출되어야 초기화가 진행되는 변수다. | |
함수를 실행하기 위한 포인터 변수를 함수 포인터라고 한다. | |
new와 malloc함수의 동작방식에는 차이가 있다. | |
구조체 변수선언 struct Car basicCar; 여기서 struct 를 생략하려면 | |
c언어는 typedef 선언을 해야하는데 c++은 그냥 Car basicCar; 이런식으로 선언가능. | |
레이블 뒤에는 ;이 아니라 :이 붙는다. | |
ex) case 1: or private: | |
멤버변수, 멤버함수 클래스 내에 선언된 변수와 함수. | |
enum, class, struct 는 { }끝에 ;를 붙여준다. | |
-> 화살표 연산자는 멤버 연산자라고 불리고 (객체, 구조체 포인터) -> (접근할 멤버) 로 쓰인다. | |
포인터에서 어떤 멤버로 접근할 때 사용한다. | |
Control Class 컨트롤 클래스 Handler Class 핸들러 클래스 | |
- 기능의 처리를 실제로 담당하는 클래스 | |
함수 포인터(Function Pointer) | |
메모리 공간엔 변수 뿐만 아니라 프로그램 실행의 흐름을 구성하는 함수들도 바이너리 형태로 저장되어 호출 시 실행이 됩니다. | |
그리고 이렇게 메모리상에 저장된 함수의 주소 값을 저장하는 포인터 변수가 바로 '함수 포인터 변수' | |
모든 함수는 프로그램 실행 시 '메인 메모리'에 저장되어 실행이 됩니다. | |
그리고 함수의 이름은. 이렇듯 메모리상에 저장된 함수의 주소 값을 의미합니다. | |
(배열의 이름이 배열의 시작주소 값을 의미하듯. 함수의 이름도 함수가 저장된 메모리 공간의 주소 값을 의미합니다.) | |
== 배열의 이름과 마찬가지로 함수의 이름도 상수 형태입니다!! | |
but 함수의 주소 값 저장을 위한 포인터 변수를 별도로 선언할 수 있으며. 이러한 용도로 선언된 포인터 변수를 가리켜 | |
'함수 포인터 변수' | |
int SimpleFunc(int num) | |
int (*fptr) (int) 의 경우 | |
매개변수 선언이 int 하나인 반환형 int인 함수 포인터 입니다. | |
이 함수 포인터 변수 fptr에 위의 함수 SimpleFunc의 주소 값을 저장하려면 다음과 같이 대입연산을 해야합니다. | |
fptr = SimpleFunc; | |
이렇게 대입 연산이 끝나면 fptr(3) 이 SimpleFunc(3)과 동일한 결과를 보입니다. | |
매개변수의 선언으로도 함수 포인터 변수가 올 수 있습니다! | |
형(Type)이 존재하지 않는 void 포인터 | |
void * ptr; | |
이러한 포인터 변수를 가리켜 'void형 포인터 변수'라고 합니다. | |
void 포인터 변수는 무엇이든 담을 수 있어서 어떠한 변수의 주소 값이든 담을 수 있습니다!! | |
int num = 20; void SimpleFunc(void); 이런 식의 변수와 함수 두개가 선언 되어 있을 때 | |
void * ptr; | |
ptr = # ptr = SimpleFunc; 각각의 주소 값을 받을 수 있으며 | |
ptirntf("%p \n", ptr); 로 출력할 수 있습니다. | |
즉 함수든 변수든 모두 다 담을 수 있다는 것입니다. | |
하지만 void형 포인터 변수를 가지고는 아무런 포인터 연산도 하지 못하는 것이 치명적 단점입니다. | |
(값의 변경이나 참조도 불가능) | |
따라서 주소 값만 의미를 두고 포인터의 형은 나중에 결정하고 싶을 때 void 포인터를 사용합니다!! | |
+ 비쥬얼스튜디오에서 alt+ender 누르면 속성창 나오고 빌드에서 제외 '예'라고 쓰면 한 프로젝트내에 여러 소스 만들어둘수있다. | |
+ shift로 선택해 한방에 할수도 |
[C++11]
-
랜덤 객체
#include <random>
C++11의 표준으로 등록된 난수 추출 방식으로, 기본적인 난수 엔진으로 메르센 트위스터 엔진이 사용된다.
기존 rand() 업그레이드 버전이라 생각하면 됨.- 랜덤 생성기 객체 선언 std::default_random_engine 객체명;
- 만약 실수난수가 필요하다면 std::uniform_real_distribution 을, 정수난수가 필요하다면 std::uniform_int_distribution 을, 정규분포를 갖는 랜덤값이 필요하다면 std::normal_distribution 등 함수선언.
- 선언한 함수의 인자에 생성기를 사용해서 난수 생성/사용.
*rand()과 다르게 std::uniform_int_distribution에서 0, 10 범위를 지정했다면 0, 9까지가아니라 10도 출력가능하다는 것을 기억해야한다.
ex)
std::default_random_engine generator;
std::uniform_int_distribution<int> randomRow(0, image.rows - 1);
std::uniform_int_distribution<int> randomCol(0, image.cols - 1);
int i = randomRow(generator);
int j = randomCol(generator);
MFC(Microsoft Foundation Class library) - 윈도우 개발 라이브러리라고 생각하면 됨.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
C++ STL (Standard Template Library) - C++ 여러 라이브러리들을 뜻한다.
Vector 자료형
push_back() 뒤에 집어넣는 것
쓸땐 배열처럼 []쓸수있다.
vector v(n) v는 기본값으로 초기화된 n개의 원소를 갖는다.
vector v(n,x) v는 x 값으로 초기화된 n개의 원소를 갖는다.
.size() 원소 개수
http://hyeonstorage.tistory.com/324
#include <algorithm>
min max swap 함수 사용가능반복자(iterator)
데이터 컬렉션을 순회하기 위해 만들어진 탐색용 전문 클래스, 각 요소를 순회하는 방법을 숨김과 정보은닉 원칙을 사용하므로 컬렉션을
더욱 쉽고, 안전하게 조회할 수 있다. 게다가 컬렉션 타입에 관계없이 비슷하게 사용할 수 있다.