C++를 공부해보자:)

dqQQQ

·

2023. 11. 30. 10:57

개요


반복문, 조건문 같은 다른언어에도 있는 cpp의 기초를 다지고

인라인함수, 참조 변수, 오버로딩, 디폴트 매개변수, 함수 템플릿, 참조, 예외에 대해 알아보겠다.




Cpp란


C의 절차적 언어 + 클래스를 추가한 객체지향 언어 + 템플릿이 지원하는 일반화 프로그래밍언어




변수


  • 변수 이름에는 영문자, 숫자, 밑줄문자만 사용가능
  • 숫자를 변수 이름의 첫문자로 사용 불가능
  • 변수이름에서 대소문자 구별
  • c++ 키워드는 변수이름으로 사용 불가능
  • __ 혹은 _대문자로 시작하는 이름은 컴파일러와 리소스가 사용함
  • _로 시작하는 이름은 컴파일러와 리소스가 전역 식별자로 사용함




상수


상수로 선언하면 컴파일러는 값을 변경하려하지 않는다.

상수는 선언과 동시에 초기화해야한다.




자료형


int형은 컴퓨터에서 가장 자연스럽고 효율적인 정수 크기로 설정된다.

따라서 가급적이면 int로 선언한다.

숫자 앞에 0x를 붙이면 16진수 정수이고 0을 붙이면 8진수 정수로 저장된다.

cout << hex;를 하면 출력하는 진수를 바꾼다.

auto를 사용하면 자동으로 자료형을 정한다. STL에서 유용하게 쓰인다.




형변환


작은 데이터로 초기화 해도 큰 데이터 타입으로 들어간다.

(long) examplelong (example)이 있다.

앞은 C 스타일 뒤는 C++ 스타일이다.




구조체


C언어에서는 초기화 할때 sturct 키워드를 붙이고 해야하지만 c++은 안해도된다.

struct 구조체이름 구조체변수는 C

구조체이름 구조체변수는 C++




공용체


서로다른 자료형을 한 번에 한가지만 보관할 수 있는 데이터형

공용체의 크기는 가장 큰 멤버의 크기가된다. 메모리의 절약을 위해 사용한다.




열거체


const로 기호상수를 만드는 다른 방법이다.

이것들로 산술 연산은 허용되지 않는다. 숫자 + 열거체는 가능.




new


선언에 의해 배열을 생성하면 컴파일 할 때 만들어지고 정적 바인딩이라고 함

new를 이용하면 실행시간에 생성되고 동적 바인딩이라고함




배열의 대안


  • Vector 템플릿 : 동적배열
  • array 템플릿 : 정적배열




bool


cout.setf(ios_base::boolalpha);를 적으면 1, 0이아니라 true, false를 출력




switch


선택 사항이 3개 이상이면 swit초 쓰는게 실행속도면에서 효율적




함수 프로시저


엄밀히 말하면 리턴값이 있으면 함수 없으면 프로시저 근데 그냥 다 함수라고 부름




함수원형을 사용해야하는 이유


  • 함수원형은 컴파일러에게 함수의 인터페이스를 미리 알려준다.
  • 계산을 끝냈을 때 리턴값을 어떤 지정된 위치에 넣고 값을 가져온다. 미리 알려줘야 몇바이트 꺼내고 어떻게 처리해야하는 지 안다.




포인터와 const


// 사용가능 그러나 값은 변경 불가능
int age = 25;
const int *pt = &age;

const int earth = 9;
const int *p =  &earth;

// 사용불가능
const int moon = 2;
int *p = &moon;

가능하면 const를 사용하라

int sloth = 3;
const int *ps = &sloth; // const int를 가리키는 포인터
int * const finger = &sloth; // int를 지시하는 const 포인터
const int * const p = &sloth; // const int를 가리키는 const 포인터




이름 공간


C++에서는 이름의 사용범위를 더욱 잘 제어할 수 있도록 이름 공간이라는 기능을 제공한다.

이름 공간은 전역위치 또는 다른 이름 공간 안에 넣을 수 있지만 블록 안에는 놓을 수 없다.

이름 공간에 속해있는 이름에 접근하려면 사용 범위 결정 연산자인 ::를 이용한다.

사용할 때마다 제한하는 것은 귀찮으므로 using 선언과 using 지시자라는 두가지 방법을 제공한다.

using 선언은 하나의 특별한 식별자를 사용할 수 있게 만들고, using 지시자는 그 이름 공간 전체에 접근할 수 있게 만든다.

using Q::fetch;를 선언하면 그 이후는 fetch만으로 사용가능하다.

using 지시자는 using namespace q라고 사용하며 이름공간에 속한 모든 이름을 사용할 수 있다.

그러나 이것들은 이름 충돌의 가능성을 증가시긴다.

그러므로 전역 이름공간을 만들기보단 using 선언을 사용하여 필요한 것만 사용하는 것이 좋다.

이름공간 안에 이름공간을 중첩해서 선언할 수 있다. q::kyu::jin와 같이 사용할 수 있다.

이름공간의 이름을 생략하면 이름없는 이름공간이 만들어진다.

그러므로 ::없이도 접근할 수 있다.




이름 공간 사용 가이드라인


  • 정적, 외부 전역변수를 사용하지 말고 이름이 명명된 이름 공간에 있는 변수를 사용
  • 함수 또는 클래스 라이브러리를 개발한다면 하나의 이름 공간안에 넣어라
  • using 지시자는 왠만하면 사용하지 마라
  • using 지시자를 헤더에 사용하지마라

cpp의 입출력


c++ 프로그램은 입력과 출력을 바이트들의 스트림이라고 간주한다.

입력 시 프로그램은 입력 스트림으로부터 바이트들을 추출하고, 출력시는 삽입한다.

하나의 스트림을 프로그램의 입력에 연결하고 하나의 파일에 연결한다.

즉 입력 스트림은 양쪽에 연결을 한다. 아래의 그림과 같다.

 

한 객체를 생성하면 하나의 스트림이 열리고 자동으로 버퍼가 생성되고 그 버퍼가 스트림에 연결된다.

그러고 나면 클래스의 멤버 함수들을 사용할 수 있게 된다.




cin


cin은 입력을 수행하는 객체이다. cin은 iostream 파일에 입력스트림을 나타내는 객체로 정의 되어있다.

화이트 스페이스가 아닌 문자가 나올 때까지 건너뛴다.

그리고 데이터형과 일치하지 않는 문자에 도달할 때까지 읽는다.

int elevation;
cin >> elevation;
//위와 같은 코드에 -123Z를 넣었다고 가정하면
//Z는 입력스트림에 그대로 남겨져있고 elevation에는 -123이 대입된다.




cin flag


cin은 goodbit, failbit, badbit, eofbit 총 4개의 플래그 비트가 있다.

각각의 멤버 함수를 사용하면 비트가 켜졌는지 알 수 있다. cin.eofbit()를 사용하면 켜졌을 때 true를 반환한다.

failbit는 복구할 수 있는 비트 badbit는 복구할 수 없는 심각한 오류일 때 켜진다.

eofbit는 단어에서 알 수 있듯이 EOF를 감지한다.

cin 메서드가 EOF를 발견하면 플래그가 켜지고 cin은 더이상 입력을 받아들이지 않는다. 그렇기 때문에 무한 루프가 된다.

이때 cin.clear() 메서드는 EOF 플래그를 지우고 다시 입력이 진행될 수 있도록 한다.

플래그비트가 복구되었다면 스트림의 버퍼가 보유하고 있는 내용들을 cin.ignore() 함수를 이용하여 지울 수 있다.




cin flag on시에 상태


부적절한 입력이 들어왔을 때 cin 또는 cout 객체는 스트림 상태를 나타내는 하나의 데이터 멤버를 가지고 있다.

스트림 상태는 eofbit, badbit, failbit라는 세게의 ios_base 원소로 이루어진다.

cin연산이 파일의 끝에 도달하거나 ctrl + d를 감지하면 eofbit가 감지된다.

cin이 기대하는 문자를 읽지 못하거나 접근할 수 없는 파일을 읽을 때, 쓰기방지된 디스크에 쓰려할 때도 failbit가 감지된다.

badbit는 알수 없는 원인으로 스트림이 손상되었을 때 설정된다.

 


cin 상태 설정


clear()과 setstate()가 있다. 둘다 상태를 설정한다는 기능은 같지만 설정 방법이 다르다.

cin.clear()를 호출하면 3개의 상태비트를 모두 해제한다.

만약 매개변수를 넣으면 그 매개변수의 상태와 다른 비트들의 상태를 같게한다.

그러나 setstate()에 매개변수를 넣으면 해당하는 매개변수만 비트를 해제한다.




get(ch) vs get()


get(char &)와 get()는 화이트 스페이스를 건너뛰지 않는 단일 문자입력기능을 제공한다.

매개변수가 있다면 입력 문자를 대입하고 없다면 입력문자를 int형으로 변환하여 리턴값으로 사용한다.

파일이 끝났을 때 리턴값은 get(ch)는 false로 변환하고 get()은 EOF를 리턴한다.




클래스


OOP는 프로그램 설계 방법의 하나이다.

  • 추상화
  • 캡슐화
  • 다형성
  • 상속
  • 코드의 재활용

객체 지향 프로그래밍은 위의 기능들을 지원한다.

c에 OOP기능을 추가한 c++을 사용하는 가장 큰 이유중의 하나. 클래스에 대해 알아보겠다.

추상화란 정보를 사용자 인터페이스로 표현하는 것이다.

데이터 객체에 필요한 메모리의 크기를 결정하고 메모리를 어떻게 해석할지 결정하고 수행할 수 있는 연산을 결정한다.

클래스는 추상화를 사용자 정의 데이터형으로 변환해 주는 c++의 수단이다.

클래스는 데이터 표현과 데이터를 메서드들을 하나의 패키지 안에 결합한다.

클래스 설계는 public 인터페이스와 세부적인 구현을 분리한다.

public 인터페이스는 설계의 추상화를 나타낸다.

private을 이용해서 데이터은닉과 세부적인 구현들을 따로 결합하여 추상화와 분리하는 것을 캡슐화라고 한다.

클래스 안에서는 private이 디폴트이기 때문에 적지 않아도 된다.




접근제어


  • public : 특정 클래스의 객체를 사용하는 프로그램은 그 객체의 public 부분에는 직접 접근할 수 있다.
  • private : friend 혹은 public 멤버 함수를 통해서만 접근가능
  • protected : 상속 받은 자식들만 접근 가능




멤버 함수


멤버 함수를 정의할 때 그 멤버 함수가 어느 클래스에 속하는지를 나타내기 위해 ::를 사용해야한다.

클래스의 메서드는 private 부분에만 접근할 수 있다.

void Stock::update(double price)라고 써야하며 Stock클래스 안에 있는 update 함수라고 이해한다.




생성자와 소멸자


데이터들이 private 접근 제어를 가지고 있어 프로그램이 데이터 멤버에 직접 접근할 수 없기 때문에 멤버함수를 이용한다.

생성자의 이름은 클래스의 이름과 같다. 객체를 선언할 때 프로그램이 자동으로 생성자를 호출한다.

m_를 private멤버 이름 앞에 붙이는 것이 관례.

디폴트 생성자를 사용자가 제공해야한다.

소멸자는 컴파일러가 알아서 인스턴스를 없애준다. 사용자가 명시적으로 사용하면 안된다.




this 포인터


클래스의 각 멤버함수가 두개의 객체를 동시에 처리할 때 this를 쓴다.

모든 클래스 메서드들은 그 메서드를 호출하는 객체의 주소로 설정되는 하나의 this 포인터를 갖는다.

자기 자신의 멤버에 접근한다는 것을 명시적으로 할 때 사용한다.

즉 this는 그 객체의 주소이다.

 


상속


기초 클래스라 불리는 클래스에서 모든 메서드와 멤버들을 상속받아, 파생 클래스를 만들 수 있는 기능을 말한다.

클래스를 선언할 때 클래스 이름 옆에 초기화 리스트처럼 : public ClassName처럼 쓰면된다.

파생 클래스는 기초 클래스의 private 멤버에 직접 접근할 수 없고 protected 멤버 혹은 public 멤버만 접근 가능하다.

파생 클래스의 객체를 생성할 때, 프로그램은 기초 클래스 생성자를 호출하고 그 다음에 파생 클래스 생성자를 호출한다.

상속받는 데이터들의 초기화는 기초 클래스가 한다. 파생클래스의 객체가 소멸될 때, 파생 클래스의 소멸자부터 호출한다.

기초 클래스 포인터 혹은 참조는 명시적으로 데이터형변환을 안해줘도 파생 클래스의 객체를 지시할 수 있다.

그러나 반대로 작은거에 큰거 담으려 할 때는 안된다.

 


업캐스팅 다운캐스팅


파생 클래스 참조나 포인터를 기초 클래스 참조나 포인터로 변환하는 것을 업캐스팅이라고 한다.

public 상속에서는 명시적인 데이터형변환이 없어도 언제든 가능하다.

그러나 반대로 다운캐스팅 시에는 명시적인 데이터형변환이 있어야 가능하다.




virtual


만약 객체에 따라 메서드의 행동이 달라진다면 이것을 다형성이라고 부르며 virtual키워드를 사용하여 해결할 수 있다.

만약 virtual 키워드를 사용하지 않은 경우, 프로그램은 참조형이나 포인터형에 기초하여 메서드를 선택하지만, 사용한다면 객체에 따라서 메서드를 선택한다.

기초 클래스에서 가상으로 선언되었다면 파생클래스에도 자동으로 가상메서드가 된다.

생성자는 가상으로 선언할 수 없다.

또 소멸자에 virtual을 붙여 가상 소멸자로도 많이 사용한다. 이는 파생클래스 객체가 소멸될 때 소멸자들이 올바른 순서로 호출되게해준다.

 


가상 소멸자


소멸자들이 가상이 아니라면, 포인터형에 해당하는 소멸자만 호출될 것이다. 가상이라면 객체에 해당하는 소멸자가 호출 된다.

즉 기본 클래스의 소멸자부터 호출될 것이고 파생 클래스의 객체는 메모리 누수가 될 것이다.




정적 바인딩과 동적 바인딩


소스 코드에 있는 함수 호출을 특정 블록에 있는 함수 코드를 실행하라는 뜻으로 해석하는 것을 함수이름을 바인딩한다라고 한다.

이는 컴파일러가 수행하는데 컴파일 동안 일어나는 바인딩을 정적 바인딩이라고 부른다.

가상함수는 컴파일 시간동안 어떤 함수를 사용할 지 알 수가 없다. 프로그램 실행할 때 사용자가 객체를 결정할 때 알 수 있다.

따라서 프로그램을 실행할 때 가상 메서드가 어떤 메서드를 실행할 지 정한다. 이를 동적 바인딩이라고 부른다.

 


왜 두가지가 있는가?


동적 바인딩은 클래스 메서드들을 다시 정의하는 것을 허용하지만 정적결합은 허용하지 않는다.

대부분의 경우에서 동적 바인딩이 좋다고 하는데 왜 디폴트는 정적일까??

효율성과 개념모델 때문이다.

프로그램이 실행 시간에 결정하도록하면 부담이 생긴다. 그리고 정적 바인딩이 프로그램적으로 효율적이기 때문에 디폴트이다.

 


가상함수는 어떻게 작동하는가


이는 컴파일러가 수행한다. 컴파일러는 각각의 객체에 멤버를 하나씩 추가한다.

멤버는 함수의 주소들로 이루어진 배열을 가리키는 포인터이다. 이를 가상함수 테이블이라고 부른다.

클래스의 객체들으르 위해 선언된 가상 함수들의 주소가 저장되어있다.

파생 클래스가 가상 함수를 다시 정의하지 않으면 테이블은 함수의 오리지널 버전의 주소를 저장한다.

파생 클래스가 다시 정의한다면 테이블에 추가되는 형식이다.

따라서 가상함수를 사용하면 메모리와 실행 속도 면에서 부담이 생긴다.

이 테이블은 객체단위가 아닌 클래스 단위로 존재한다.




인라인함수


인라인 함수는 프로그램의 실행 속도를 높이기 위해 사용한다.

보통 함수를 실행하면 새로운 스택안에서 처리를 하고 기존의 주소로 돌아온다.

이렇게하면 실행제어를 넘기는 사이사이에 지연이 발생하고 사용 함수가 많다면 속도는 낮아진다.

인라인함수로 선언하면 컴파일러는 그에 대응하는 함수 코드로 대체한다.

그만큼 메모리를 많이 차지하므로 신중하게 사용해야한다.

보통 함수 코드가 적으며 실행 횟수가 많다면 사용한다.

일반적인 관행은 원형을 아예 생략하고, 원형이 놓일 자리에 함수 코드 전체를 놓는 것이다.

#include <iostream>

inline double square(double x) { return x * x; }
using namesapce std;

int main()
{
  double a, b;
  double c= 13.0;
  a = square(5.0);
  b = square(4.5 + 7.5);
  cout << a << b << endl;
}

c에서 사용하는 매크로랑 비슷한 기능이나 더 강력한 기능이므로 매크로보다 인라인함수 사용을 권장




참조


참조(reference)는 미리 정의된 어떤 변수의 별명이다.

참조의 주된 용도는 함수의 매개변수에 사용하는 것이다.

참조를 매개변수로 사용하면, 그 함수는 복사본 대신 원본 데이터를 가지고 작업한다.

참조는 선언과 동시에 초기화해야한다.

참조는 const 포인터와 상당히 비슷하다.

 


참조 매개변수 사용하는 이유


호출 함수에 있는 데이터 객체의 변경을 허용하기 위해

전체 데이터 객체 대신에 참조를 전달하여 프로그램의 속도를 높이기 위해

포인터를 사용하는 이유와 같다.

 


참조 VS 포인터


 


값만 사용하는 경우


  • 데이터 객체가 기본 데이터형이나 작은 구조체라면 값으로 전달
  • 데이터 객체가 배열이라면 포인터, const를 지시하는 포인터
  • 데이터 객체가 덩치 큰 구조체라면 const 포인터나 const 참조
  • 데이터 객체가 클래스 객체라면 const 참조

 


기존값을 변경하는 경우


  • 데이터 객체가 기본 데이터형이면 포인터
  • 데이터 객체가 배열이면 포인터
  • 데이터 객체가 구조체이면 참조나 포인터
  • 데이터 객체가 클래스 객체이면 참조




디폴트 매개변수


함수 매개변수의 디폴트 값을 설정할 수 있다.

함수 원형에 작성해야한다.

중간에 있는 매개변수를 디폴트 매개변수로 만들 수 없다. 무조건 맨 오른쪽에 있어야한다.




함수 오버로딩


디폴트 매개변수를 이용해서 오버로딩을 최대한 줄여보자




함수 템플릿


함수 템플릿은 구체적인 자료형을 포괄할 수 있는 제네릭 타입으로 함수를 정의한다.

어떤 자료형을 템플릿에 매개변수로 전달하면, 컴파일러가 그 자료형에 맞는 함수를 생성한다.

템플릿을 사용함으로써 자료형을 상황에 맞게 바꾸는 것을 자동화하여 시간을 절약하고 코드의 신뢰성을 높여준다.

template <class T>
void swap(T &a, T &b)
{
  T tmp;
  tmp = a;
  a = b;
  b = tmp;
}

위와같이 사용할 수 있고 T로 int double 등 상황에 맞는 자료형으로 변환 시켜서 함수를 작동한다.

개요


c++에서 포인터 대용으로 사용할 수 있는 참조(레퍼런스)에 대해서 알아보겠다.




참조(reference)


c++은 참조 변수라는 새로운 복합형을 언어에 추가했다.

참조는 미리 정의된 어떤 변수의 실제 이름 대신 쓸 수 있는 대용 이름이다.

참조의 주된 용도는 함수의 매개변수에 사용하는 것이다. 참조를 매개변수로 사용하면, 그 함수는 복사본 대신 원본 데이터를 가지고 작업한다.

call by value 대신 call by reference 하는 것이다.




참조변수의 생성


c와 c++에서 변수의 주소를 나타내기 위해 &기호를 사용한다 c++에서 &기호는 참조의 의미를 추가로 가지고 있다.

int a;
int &b = a;

위와 같은 코드에서 b와 a는 코드에서 같은 역할을 하며 실제로 가리키는 메모리 공간또한 같다.

참조는 선언과 동시에 초기화해야한다.

참조는 const 포인터와 상당히 비슷하다.




함수 매개변수로서의 참조


참조는 주로 함수의 매개변수로 사용된다.

참조 매개변수를 사용하면 부른 곳의 데이터가 변경될 수 있다.

만약 함수에 전달하는 정보를 사용만 하게 하려면 상수 참조를 사용하면된다.

int example(const int &a);와 같은 형식으로 사용하면 a의 값을 변경하려 할 때 에러를 발생한다.

간단한 함수는 값을 넘겨주는 것이 좋다. 그러나 구조체나 클래스와 같이 덩치 큰 데이터를 다룰 때에는 상수 참조를 사용하는 것이 좋다.

실제 매개변수와 참조 매개변수가 일치하지 않을 때 임시 변수를 생성할 수 있다. 이는 매개변수가 const 참조일 때만 허용한다.

가능하면 const로 사용하는 것이 좋다. 이유는 다음과 같다.

  1. const를 사용하면, 실수로 데이터 변경을 일으키는 에러를 막을 수 있다.
  2. 원형에 const를 사용하면, 함수가 const와 const가 아닌 실제 매개변수를 모두 처리할 수 있지만, 원형에 const가 아닌 함수는 const가 아닌 데이터만 처리할 수 있다.
  3. const 참조를 사용하면 함수가 자신의 필요에 따라 임시 변수를 생성하여 사용할 수 있다.




구조체에 대한 참조


참조는 c++의 구조체나 클래스를 다루는 데 아주 유용하다.

구조체 매개변수를 선언할 때 참조 연산자를 앞에 붙이면 된다.

struct example
{
  int a;
  int b;
};

void ex_func(example &ex);
void ex_func2(const example &ex);

위와 같은 예시로 사용된다.

리턴형이 참조일 때 함수 안에서만 사용하는 변수를 리턴하지 않도록 해야한다. 그때는 포인터를 이용해서 리턴해야한다.




참조 매개변수를 사용할 때


호출 함수에 있는 데이터 객체의 변경하거나 전체 데이터 객체 대신 참조를 전달하여 속도를 높이기 위해 사용한다.

 


값만 사용하는 경우


  • 데이터 객체가 기본 데이터형이나 작은 구조체라면 값으로 전달
  • 데이터 객체가 배열이라면 포인터, const를 지시하는 포인터
  • 데이터 객체가 덩치 큰 구조체라면 const 포인터나 const 참조
  • 데이터 객체가 클래스 객체라면 const 참조

 


기존값을 변경하는 경우


  • 데이터 객체가 기본 데이터형이면 포인터
  • 데이터 객체가 배열이면 포인터
  • 데이터 객체가 구조체이면 참조나 포인터
  • 데이터 객체가 클래스 객체이면 참조




로우레벨로 갔을 때 포인터와 참조


로우레벨 단으로 갔을 때 컴퓨터는 둘의 차이점을 모른다.

같은 어셈블리 명령어를 생성하므로 참조는 오직 인간이 포인터를 더 쉽게 사용하기 위해서 만든것이라 볼 수 있다.




예외


프로그램을 실행하다 보면 정상적으로 실행을 할 수 없는 상황이 온다. 이를 방지하기 위해 예외를 사용한다.

c언어에서는 예외가 없다. 그렇기 때문에 예외 상황을 일일이 처리해줘야하는데 abort() 호출 방법과 에러코드 리턴을 통해서 할 수 있다.

그러나 c++에는 예외라는 기능이 추가되었다. 그러나 예외는 점점 축소되는 추세라고 한다.

이유는 사람이 모든 예외사항을 컨트롤 할 수 없다는 것이다. 예외를 사용하는 프로그램에 100줄짜리 함수가 있다하면 100개의 Exit이 있는 것이다.

컴파일러가 어셈블리어로 바꿨을 때 코드의 수부터가 차이가 난다.

생성자에서는 논리적, 구조적으로 예외를 사용할 수 밖에 없다. 메모리를 할당 했는데 메모리 부족으로 못했다면? 내가 어떻게 체크하는가

그러나 나머지 부분에서는 충분히 조건문을 통해서 예외처리가 가능하다.

예외처리는 세 단계를 통해서 이루어진다.

  1. try 블럭을 실행한다.
  2. 예외를 발생시킨다.
  3. 예외 핸들러를 사용하여 예외를 포착한다.

예외에서 중요한 키워드는 throw, catch, try가 있다.

throw는 예외를 발생시킨다.

catch는 예외를 포착한다.

try는 특별한 예외들이 발생할 수 있는 하나의 코드 블록이다. 그 뒤에는 최소한 하나의 catch문이 온다.

try 블록 안에서 예외를 발생시킨다면 뒤에 오는 catch가 처리하는 구조이다.

예외발생은 함수 실행을 종료시킨다는 점은 리턴과 비슷하지만 호출한 프로그램에 제어를 넘기지 않고 try에 있는 함수를 발견할 때까지 현재 호출한 함수들을 거슬러 올라간다.

예외 클래스들의 송속 계층을 사용할 경우, 가장 늦게 만들어진 클래스의 예외가 가장 먼저 포착된다.




exception 클래스


c++ 언어에서 예외 클래스를 만들고 싶다면 부모 클래스로 사용하는 exception 클래스를 정의한다.

사용자가 상속받아 사용하면 exception 객체를 만든다.

그러면 what()이라는 가상함수가 주어진다. 이 함수는 예외 종류에 맞는 에러 원인 메세지를 리턴한다.

 


stdexcept 클래스


stdexcept는 logic_error와 runtime_error클래스를 정의한다. 이것들은 exception에서 상속받는다.

 


logic_error


  • domain_error : sin함수에 매개변수를 전달할 때 정의역이 -1 ~ 1이 아니라면 나는 에러
  • invalid_argument : 기대하지 않는 값이 함수에 전달되었을 때
  • length_error : 원하는만큼의 공간을 사용할 수 없을 때 대표적으로 string.append()의 에러
  • out_of_bounds : 인덱싱 에러

 


runtime_error


  • range_error : 언더플로나 오버플로 없이 계산 결과가 함수의 적절한 치역을 벗어날 때
  • overflow_error : 허용된 범위보다 더 많은 값을 건드릴 때
  • underflow_error : 부동소수점 계산에서 허용된 범위보다 적은 값을 건드릴 때




예외 주의사항


예외를 사용하면 프로그램의 크기가 매우 커지고 실행 속도가 떨어진다.

java에서는 다르게 c++을 아직까지 사용하는 업계는 속도, 성능을 중요시하는 분야이다.

따라서 c++을 공부할 때는 예외가 있다는 것을 알아두되 왠만한 것은 조건문과 에러코드를 통해서 처리하고 생성자등 불가피할 때만 사용하는 것이 적절하다.




nested class


클래스 안에 다른 클래스를 선언하면 내포클래스라고 한다.

클래스 선언을 내포하고 있는 클래스는 내포 클래스의 객체를 생성하여 사용할 수 있다.

내포클래스가 선언된 장소가 내포 클래스의 사용범위를 제한한다.

클래스 외부에서 사용하려면 감싸고 있는 클래스 지정자를 사용해야한다.

class a
{
  class b
  {
  ...
  };
  ...
};

a::b boy;