Programming/Programming2018. 1. 15. 03:32

예외는 데이터베이스 연결이 끊어지거나 예상하지 못한 입력을 만나는 등 정상적인 프로그램 기능에서 벗어난, 실행 중 발생하는 이상 현상

일반적으로 예외 처리는 프로그램 한 부분에서 자체적으로 해결할 수 없는 문제를 감지하고, 그 문제가 감지부에서 계속해서 프로그램을 실행할 수 없는 것일 때 사용

C++에서 예외 처리는 다음을 포함: throw 표현식, try 구역, exception 클래스



1.     Throw 표현식


감지부에서 처리할 수 없는 무언가를 만났음을 나타내는 데 사용. 이 때 throw에서 예외를 일으킨다고 함.


int num1=0;

cout << "Enter a number (100 or less integer) :";

cin >> num1;

 

if (num1 > 100)

           throw runtime_error("You must enter 100 or less integer.");

cout << num1 << endl;



100을 초과하는 입력을 넣으면 런타임 오류가 나는 것을 확인할 수 있음

위 코드에서는 100을 초과하는 입력을 받으면 runtime_error 타입 객체인 표현식을 던짐.

예외를 발생시키면 현재 함수를 마치고 이 오류를 처리할 수 있는 처리자로 제어를 옮김.

 

 


2.     Try 구역


처리부에서 예외를 처리하는 데 사용.

try 구역은 try 키워드로 시작하고 하나 이상인 catch 절로 마침.

try 구역 안에서 실행한 코드에서 발생한 예외는 일반적으로 여러 catch 절 중 하나에서 처리.

 

try 구역의 일반적인 형식은 다음과 같음


try{

           (logic 1)

}catch (exception-declaration) {

           (logic 2)

} …

 


try 구역은 try 키워드로 시작.

try 구역 다음에는 하나 이상인 catch 절 목록이 옴.

catch catch 키워드, 괄호 안에 있는 객체의 선언, 구역 이 세 가지로 구성.



int num1=0;

 

while (1) {

           try {

                     cout << "Enter a number (100 or less integer) :";

                     cin >> num1;

 

                     if (num1 > 100)

                                throw runtime_error("You must enter 100 or less integer.");

 

                     cout << num1 << endl;

                     break;

           }

           catch (runtime_error err) {

                     cout << err.what() << endl;

                     cout << "If you wanna try again, enter y:";

                     char opt;

                     cin >> opt;

                     if (opt != 'y')

                                break;

           }

}

 

return 0;

 


throw에서 설명한 바와 같이, throw가 선언되는 순간 그 에러를 처리할 처리자로 이동하여 코드를 실행함. 이는 위 예제 코드를 실행하는 것으로 알 수 있음.

여기서 중요한 것은 처리자를 찾는 동안 실행중이었던 함수를 빠져나간다는 것임.

처리자 검색은 연속적인 함수 호출과 반대임. 예외가 발생하면 그 예외를 발생한 함수를 가장 먼저 확인하고, 그 함수에서 적당한 처리자가 없으면 그 함수를 호출했던 함수를 찾아감. 이 과정을 적당한 처리자가 나올 때까지 반복함.

적합한 처리자를 찾지 못하면 terminate 라이브러리 함수로 실행을 옮기는데, 이 함수는 시스템에 따라 다르지만 프로그램을 더 이상 실행하지 않고 중단함을 보장함.

try 구역이 없으면 처리자도 있을 수 없으므로, try 구역이 없는 프로그램에서는 예외가 발생하면 terminate를 호출해 프로그램을 종료.

 

예외는 프로그램의 정상적인 흐름을 가로챔. 예외가 발생한 지점에서 보면 호출한 쪽에서 요청한 계산 중 일부만 처리하고 나머지는 처리하지 않은 채 남아 있을 수 있음. 일반적으로 프로그램에서 어떤 부분을 우회하게 되면 객체가 유효하지 않거나 불완전한 상태로 남아있으며, 또는 자원이 해제되지 않는 등의 일이 생길 수 있음. 예외를 처리하는 동안 적절히 정리하는 프로그램을 예외에 안전하다고 함. 예외에 안전한 코드를 만드는 것은 굉장히 어려운 일.

프로그램에서 예외를 처리하고 계속해서 진행하려면 일반적으로 예외가 발생할 수 있는지, 객체 유효성을 보장하고 자원이 누출되지 않으며, 적절한 상태로 프로그램을 되돌리기 위해 반드시 해야 하는 것이 무엇인지 항상 알아야 함. 




3.     표준 예외 및 Exception 클래스


C++ 라이브러리에서는 표준 라이브러리 함수에서 문제가 생겼을 때 이를 보고하는데 사용하는 여러 클래스를 정의.

이 클래스는 다음 네 가지 헤더에서 정의함.

exception: 가장 일반적인 예외 클래스인 exception을 정의. 이는 예외가 발생했다는 것만 알리고 다른 추가 정보는 알리지 않음.

stdexcept: 몇 가지 일반적인 목적의 예외 클래스를 정의함.

new: bad_alloc 예외 타입을 정의함. 자세히는 추후 설명

type_info: bad_cast 예외 타입을 정의. 이 또한 자세히는 추후 설명

 

Exception 클래스는 발생한 것에 대한 정보를 throw와 연관된 catch 사이에 전달하는 데 사용.

exception, bad_alloc, bad_cast 객체에는 기본 초기화만 할 수 있으며 이 예외 타입 객체에 초기 값을 지정할 수 있음.

다른 예외 타입은 이와 반대로 string이나 C 형식 문자열로 초기화 할 수 있으나 기본 초기화가 불가능. 그러므로 반드시 초기 값을 지정해야 하며, 이 초기 값은 발생한 오류에 대한 추가 정보를 제공하는 데 사용

 


stdexcept에서 정의하는 표준 예외 클래스

  • exception: 가장 일반적인 유형의 문제
  • runtime_error: 실행 중에만 발견할 수 있는 문제
  • range_error: 실행 중 오류 의미 있는 값 범위를 벗어나 생성된 결과
  • overflow_error: 실행 중 오류 오버플로한 계산
  • underflow_error: 실행 중 오류 언더플로한 계산
  • logic_error: 프로그램의 논리적 오류
  • domain_error: 논리 오류 결과가 존재하지 않는 인자 (ex. 루트 안에 음수 값이 들어간 경우)
  • invalid_argument: 논리 오류 적절하지 않은 인자
  • length_error: 논리 오류 해당 타입의 최대 크기보다 더 큰 객체를 생성하려 함
  • out_of_range: 논리 오류 유효한 범위를 넘어선 값 사용


Posted by BinZIP
Programming/Programming2018. 1. 13. 07:08

1.     String Library


String은 가변길이 문자열.

String을 사용하려면 string 헤더를 include 시켜야 함.

 

직접 초기화와 복사 초기화


string s1; // 빈 문자열로 초기화

string s2 = s1; // 빈 문자열 s1을 복사해 초기화

string s3 = “Hi”; // “Hi” 문자열을 복사 초기화

string s4(10, ‘c’); // ‘c’ 10개 복사본으로 초기화

string s5(“Hi”); // s3과 같이 초기화되나 직접 초기화

 

string 연산

getline(is, str); // is에서 한 줄 읽어 s에 넣고 is 반환

str.empty(); // str이 비어있으면 true, 아니면 false 반환

str.size(); // str의 문자 수를 반환

비교 연산은 대소문자를 구별하며 사전 순으로 함.

 

str의 모든 char에 대해 연산하고 싶으면 다음과 같이 사용

for(auto &c : str) // 요소에 직접 접근해 변경할 때

for(auto c : str) // 출력 등을 할 때

 

 


2.     Vector Library


Vector는 타입이 모두 같은 객체의 모음

Vector는 그 안에 다른 객체를 담기 때문에 흔히 컨테이너라고 함

Vector를 사용하려면 vector 헤더를 include 시켜야 함

 

Vector는 클래스 템플릿(Class template) – 템플릿 자체는 함수도 템플릿도 아님. 템플릿을 사용해 클래스나 함수를 만드는 과정을 인스턴스화(Instantiation)라고 함.

Vector에서 추가 정보는 담을 객체 타입.


vector<int> iv; // iv에 담을 객체는 int

vector<vector<string>> page; // page에 담을 객체는 string을 가지는 vector

 

정의 및 초기화


vector<int> v1; // int vector 하나 정의

vector<string> page{“a”, “an”, “the”}; // 목록 직접 초기화

vector<string> page = {“a”, “an”, “the”}; // 목록 복사 초기화

vector<int> ivec(10, -1); // int 요소가 10개인 ivec에 각각 -1로 직접 초기화

vector<int> ivec2(10); // int 요소가 10개이며 각각 0으로 초기화됨

vector<int> ivec3{10}; // 값이 10인 요소 1

 

Vector는 배열과 다르게 실행 중에 요소를 제한 없이 추가할 수 있음. Vector의 각 요소가 모두 같은 값이 아니라면 개수를 정해놓는 것이 오히려 성능에 나쁜 영향을 미침.

 

Vector 연산


v.empty()

v.size()

v.push_back(i);

 

String 연산과 마찬가지로 vec의 모든 요소에 대해 연산하고 싶으면 다음과 같이 사용

for ( auto &i : vec )

for( auto i : vec )

 

Size_type 타입

String Vector에서 size를 사용하면 반환되는 타입.

크기를 나타내는 부호 없는 타입. 정확히 알 필요는 없음.

 

 


3.     Iterator


반복자(Iterator)를 사용하면 객체에 간접적으로 접근할 수 있음

포인터와는 다르게 반복자는 주소 연산자를 사용해 얻지 않음 이름이 begin end인 멤버를 통해

end에서 반환한 반복자는 연관된 컨테이너의 마지막 요소 바로 다음에 위치한 반복자임 흔히 off-the-end iterator(끝 지난 반복자) 혹은 end 반복자라고 말함

auto b = v.begin(), e = v.end();

 

Iterator 연산

++, -- : iterator가 가리키고 있는 컨테이너 요소의 전, 후를 지시하도록 할 수 있음

-> : iterator를 역참조해 대상 요소에서 멤버를 가져올 수 있음.

* : iterator가 나타내는 요소에 대한 참조자를 가져올 수 있음.

 

Iterator 타입

size_type과 비슷하게 정확하게 알 필요 없음. 하지만 반복자가 있는 라이브러리 타입에서는 실제 반복자 타입을 나타내는 iterator, const_iterator라는 타입 정의

const_iterator const 포인터처럼 행동함

 

역참조와 멤버 접근 결함

반복자를 역참조하면 그 반복자가 나타내는 객체를 얻음. 만약 객체가 클래스 타입이면 그 객체의 멤버에 접근할 수 있음.

*it.empty() // 오류. it iterator이기 때문에 empty 멤버 없음.

위와 같은 표현식을 간단히 하기 위해 ->연산자를 정의.

it->empty()

 

일부 vector 연산은 반복자를 무효화함. vector는 동적으로 커질 수 있기 때문에 크기가 변하면 해당 vector에 속한 모든 반복자가 무효화될 수 있음. 자세히는 추후에 설명

 

반복자 산술 연산

string vector에 대한 반복자에서는 한 번에 여러 요소를 이동하는 연산 가능

두 반복자의 빼기 연산은 오른쪽 반복자에 더해 왼쪽 반복자가 되는 수를 반환함. 그래서 중앙점에 가장 가까운 요소를 나타내는 iterator를 계산하는 식은 다음과 같음

auto mid = v.begin() + v.end() / 2

 

Posted by BinZIP
Programming/Programming2018. 1. 11. 04:00

1.   복합 타입


복합 타입(Compound Type)은 다른 타입을 사용해 정의한 타입. C++에서는 대표적으로 참조자와 포인터가 있다.


-       참조자

참조자(Reference)는 객체에 별칭을 정의함.

참조자로 참조하는다른 타입이 곧 참조자의 타입.

참조자 타입을 정의할 때는 선언자를 &d 형식으로 쓰는데, 여기서 d는 선언할 이름.


int num = 10;

int &refNum = num;

refNum += 2; // num 2를 더하는 것과 같다.

int &refNum2; // 오류. 참조자는 초기화해야 한다.

int ii = refNum; // ii = num과 같다.


변수를 초기화할 때는 초기 값을 생성하는 객체에 복사해 넣지만, 참조자를 정의할 때는 초기 값을 복사하는 것이 아닌 참조자를 초기화식에 결합함. 일단 초기화 한 후에는 참조자는 자신의 초기 객체와 결합한 채로 남기 때문에 반드시 초기화 해야 함.

참조자는 별칭. 객체가 아니며, 이미 존재하고 있는 객체에 대한 다른 이름이라고 보아야 함.

 

-       포인터

포인터는 다른 타입을 가리키는 복합 타입.

참조자처럼 다른 타입에 직접적으로 접근하는데 사용됨. 하지만 참조자와는 다르게 포인터는 그 자체로 객체임. , 초기화 하지 않고 정의 가능.

포인터는 대입하거나 복사할 수 있으며, 포인터 하나가 생명 주기 동안 여러 다른 객체를 가리킬 수 있음.


void* 포인터

void* 타입은 모든 객체의 주소를 담을 수 있는 특별한 포인터 타입. 가리키는 객체의 타입은 알 수 없음.

할 수 있는 일이 제한적. 가리키는 객체에 연산을 할 수 없음.

일반적으로 메모리를 메모리로 다루기 위해서 사용함.

 

참조자는 객체가 아니기 때문에 참조자에 대한 포인터는 존재하지 않지만, 포인터는 그 자체로 객체이기 때문에 포인터에 대한 참조자는 존재할 수 있음.

 



2.   Const 한정자


변수의 값을 바꿀 수 없도록 정의하고 싶을 때 사용.


상위와 하위

포인터 그 자체가 const일 경우 top-level(상위) const, 가리키는 대상 객체가 const일 경우는 low-level(하위) const라고 함.


int i=10;

const int ci = 100;

int *const p1 = &i; // 상위 const

const int *p2 = &ci; // 하위 const

 

객체를 복사할 때 상위 const는 무시하지만, 하위 const는 절대 무시하지 않음. 객체를 복사할 때, 두 객체 모두에 하위 const 한정 표시가 있거나 두 객체 타입을 서로 변환할 수 있어야 함.

일반적으로 const가 아닌 객체는 const 객체로 변환할 수 있지만, 그 반대는 아님.

 

constexpr과 상수 표현식

상수 표현식(constant expression)은 컴파일 중에 값을 평가할 수 있으며, 그 값을 바꿀 수 없는 표현식.

객체나 표현식이 상수 표현식인지 여부는 타입과 초기 값에 따름.


const int a = 20; // O

const int b = a+1; // O

int c = 27; // X, 값이 바뀔 수 있음

const int d = getScale(); // X, 초기 값을 알 수 없음

 

constexpr 변수

변수가 상수 표현식인지 컴파일러에서 확인하게 할 수 있음.

constexpr로 선언한 변수는 암시적으로 const이므로 상수 표현식으로 초기화해야 함.


constexpr int a = 20; // O

constexpr int b = a+1; // O

constexpr int c = getScale(); // getScale constexpr 함수 일 때만 가능

 

상수 표현식은 컴파일 중에 평가할 수 있으므로 constexpr 선언에 쓸 수 있는 타입은 제한적.

constexpr을 사용할 수 있는 타입을 상수타입(literal type)이라고 함.

 

constexpr 선언에서 포인터를 정의하면 constexpr는 포인터가 가리키는 타입이 아닌 포인터 자체에 적용됨.


const int* p = i;

constexpr int* q = i;

 

p const int형에 대한 포인터, q int형에 대한 const 포인터가 됨.

 



3.   그 외


-       타입 별칭(Type alias)

typedef 혹은 using을 사용해 복잡한 타입 정의를 간단하게 할 수 있음.


typedef unsigned int UINT;

using UINT = unsigned int;


using을 사용해 타입 별칭을 정의하는 것을 별칭 선언(alias declaration)이라 함.

 

-       Auto 타입 지정자

auto 타입 지정자는 int, double과 같은 특정 타입을 명명하는 타입 지정자와 달리 컴파일러에서 초기 값을 통해 타입을 추론하도록 함. 그러므로, auto를 타입 지정자로 사용하는 변수에는 반드시 초기 값이 있어야 함.

auto는 일반적으로 상위 const를 무시하므로, 추론한 타입에 상위 const가 있으려면 명시적으로 지정해야 함.


int i=0;

const int ci = i;

auto a = &ci; // aconst int*.

const auto b = &ci; // bconst int* const

 


-       decltype 타입 지정자

변수를 초기화 하는 데 표현식을 사용하는 것이 아니라 컴파일러에서 표현식으로부터 추론한 타입으로 변수를 정의하고 싶을 때 사용.

컴파일러에서는 타입을 결정하기 위해 표현식을 분석할 뿐, 평가하지는 않음.

decltype(func()) sum = a; // sum의 타입은 func가 반환하는 타입

decltype을 적용하는 타입이 변수이면 상위 const와 참조자를 포함해 대상 변수의 타입을 반환함.

적용하는 타입이 변수가 아닌 표현식이면 해당 표현식에서 반환하는 타입을 얻음.


int r=0, *pr=&r, &i = r;

decltype(r) a=0;

decltype(r+1) c = 1;

decltype(i) d = c;

decltype(*pr) b = a;


dint& 타입, b 또한 int& 타입이므로 초기화는 필수적임.

 

 

Posted by BinZIP