반응형

std::cin

  • 입력값 확인 및 재입력 처리
#include <iostream>

int main()
{
    int n = 0;

    while(1)
    {
        std::cin >> n; //숫자 입력을 기대하였으나 사용자가 문자를 입력 하였을때

        if (std::cin.fail()) // 정상적인 입력 여부 확인
        {
            std::cout << "실패" << std::endl;
            std::cin.clear(); // cin의 상태를 나타내는 비트 초기화
            std::cin.ignore(256, '\n'); // 내부적으로 사용되는 입력 버퍼 제거, \n만날때까지 최대 256자 제거
            continue;
        }
        break;
    }

    std::cout << n << std::endl;
}

std::cin

  • 1개의 단어 입력 처리
#include <iostream>
#include <string>

int main()
{
    std::string w;
    std::cin >> w; // 1개의 단어 입력
        
    std::cout << w << std::endl;
}
  • 문장(다중 단어) 입력 처리
#include <iostream>
#include <string>

int main()
{  
    std::string s;
    std::getline(std::cin, s); // 문장 전체 입력

    std::cout << s << std::endl;
}
반응형

반응형

예외(Exception) 기본

  • throw, try, catch
  • throw를 사용해서 예외를 던짐
  • throw된 예외는 반드시 잡아서 처리해야 하며, 처리하지 않은 경우 abort() 함수를 수행하고 종료됨
  • try ~catch 구문을 사용해서 예외 처리

Catch(...)

  • 모든 종류의 예외를 잡을 수 있음
  • catch를 여러개 만들때는 반드시 마지막에 놓아야 함
#include <iostream>

int foo()
{
    if (1) // 예외조건 가정
        throw 1;

    return 0;
}

int main()
{
    try
    {
        foo();
    }
    catch (int n)
    {
        std::cout << "예외 발생" << std::endl;
    }
    catch (...)
    {
        std::cout << "... 예외 발생" << std::endl;
    }
    std::cout << "계속실행" << std::endl;
}

std::exception

  • C++ 표준 예외의 최상위 클래스
  • what() 가상함수 제공
#include <iostream>

class MemoryException : public std::exception // 예외 클래스 정의, std::exception 상속
{
public:
    virtual const char* what() const noexcept // std::exception의 가상함수 what() 구현
    {
        return "메모리 예외";
    }
};

int foo()
{
    if (1) // 예외조건 가정
        throw MemoryException(); // 사용자 정의 예외 클래스 throw

    return 0;
}

int main()
{
    try
    {
        foo();
    }
    catch (MemoryException & e)
    {
        std::cout << e.what() << std::endl;
    }

    std::cout << "계속실행" << std::endl;
}

C++ 표준 예외 클래스

  • std::bad_alloc
  • std::range_error
  • std::out_of_range
#include <iostream>

int main()
{
    try
    {
        int* p = new int[100]; // C++ 메모리 할당 실패시 예외 발생
    }
    catch (std::bad_alloc & e)
    {
        std::cout << e.what() << std::endl;
    }
}

noexcept

  • 함수에 예외가 없음을 명시적으로 표기하는 방법
  • throw() 동일 역할
  • 명시 할경우 컴파일러 최적화시에 도움됨
  • noexcept() 함수로 예외 없음 여부를 조사 할 수 있음
#include <iostream>

// 예외가 없는 함수
void a() noexcept {}
void a1() throw() {}

int main()
{
    bool ba = noexcept( a() ); //예외 없음 여부 조사 가능
    bool ba1 = noexcept( a1() );
    std::cout << ba << "," << ba1 << std::endl;
}
반응형

반응형

다중 상속(Multiple Inheritance)

  • C++언어에서 2개 이상의 기반 클래스로 부터 상속 받을 수 있음
  • 다른 객체지향 언어에서는 허용되지 않는 경우가 많음
  • 기반 클래스의 멤버 이름이 동일한 경우 주의
#include <iostream>

struct A
{
    int a;
};
struct B
{
    int a;
};
struct C : public A, public B // 다중 상속
{
    int c;
};

int main()
{
    C c;
    // c.a = 10; 2개의 기반클래스에서 같은 멤버 이름을 사용하고 있으므로 ambiguous error
    c.A::a = 10; // 상속 받은 A 클래스의 a 멤버에 대입
    c.B::a = 20; // 상속 받은 B 클래스의 a 멤버에 대입
}

가상 상속(Virtual Inheritance)

  • 다이아몬드 처럼 다중 상속 기반 클래스가 같은 기반클래스를 상속 받아서 구현된 경우 동일 멤버를 이중 속상 받지 않도록 가상 상속을 사용해야 함

다이아몬드 상속

#include <iostream>

struct X
{
    int x;
};
struct A : public X // X의 멤버 x를 상속
{
    int a;
};
struct B : public X // X의 멤버 x를 상속
{
    int b;
};
struct C : public A, public B // 다중 상속
{
    int c;
};

int main()
{
    C c;
    c.x = 10; // 에러 X의 멤버인 x를 A, B 클래스가 상속받은 상태로 접근이 모호
}
#include <iostream>

struct X
{
    int x;
};
struct A : virtual public X // 가상 상속
{
    int a;
};
struct B : virtual public X // 가상 상속
{
    int b;
};
struct C : public A, public B // 다중 상속
{
    int c;
};

int main()
{
    C c;
    c.x = 10; // A, B를 가상 상속으로 바꿔주면 한곳에서 상속만 보장 되므로 해결
}
반응형

반응형
  • Down Casting
    • upcasting된 포인터를 원래의 타입으로 캐스팅 하는것
  • static_cast
    • 컴파일시간 캐스팅
    • 기반 클래스의 포인터가 실제 어떤 타입의 객체를 가리키는지 조사 할 수 없음
  • dynamic_cast
    • 실행시간 캐스팅
    • 잘못된 down casting 사용시 0 반환
    • 가상 함수가 없는 타입은 dynamic_cast를 사용할 수 없음
#include <iostream>
#include <typeinfo>
#include <typeindex>

class Animal 
{
public:
    virtual ~Animal() {}

};
class Dog : public Animal 
{
public:
    int color;
};

void foo(Animal* p)
{
    // 선 타입 비교, 후 static_cast
    if (typeid(*p) == typeid(Dog))
    {
        Dog* pDog = static_cast<Dog*>(p);
    }

    //OR

    // 선 dynamic_cast, 후 nullptr 비교
    Dog* pDog = dynamic_cast<Dog*>(p);
    if (pDog == nullptr)
    {
        std::cout << "nullptr" << std::endl;
    }

    std::cout << pDog << std::endl;

}

int main()
{
    Animal a; foo(&a);
    Dog d; foo(&d);
}

RTTI를 사용하지 않고 추가 기능을 제공하는 방법도 생각 필요

#include <iostream>

class Animal 
{
public:
    virtual ~Animal() {}
};
class Dog : public Animal
{
};

void foo(Animal* p)
{
}

void foo(Dog* p)
{
    foo(static_cast<Animal*>(p)); // 기존 공통 함수 기능 지원
    // Dog에 대한 특정 기능은 여기 추가 구현
}

int main()
{
    Animal a; foo(&a);
    Dog d; foo(&d);
}
반응형

반응형
  • typeid
    • 타입에 대한 정보를 얻을때 사용하는 연산자
    • <typeinfo> 헤더 파일을 포함해야 함 // 컴파일러에 따라 생략 가능(표준 스팩은 명시 해야함)
    • typeid(타입)
    • typeid(객체)
    • typeid(expression)
    • typeid의 결과로 const std::type_info& 반환
    • 가상 함수가 없는 객체(non-polymorphic) : 컴파일 시간에 타입 조사
    • 가상 함수가 있는 객체(polymorphic) : 실행시간에 타입을 조사(가상함수 테이블 정보 참고)
  • std::type_info
    • 타입의 정보를 담고 있는 클래스
    • 사용자가 직접 객체 생성할 수 없고 typeid() 연산자를 통해서만 얻을 수 있음
    • 멤버 함수인 name()을 통해서 타입의 이름을 얻을 수 있음
    • 실행 결과는 컴파일러 마다 표현 바법이 다름
    • g++로 생성된 실행파일의 경우 "a.exe | c++filt -t"로 실행하면 완전한 타입의 이름을 볼수 있음
  • RTTI(Run Time Type Information)
    • 실행 시간에 타입의 정보를 조사하는 기법
  • std::type_info로 동일 타입 조사 방법
    • t1 == t2 : ok
    • t1.hash_code() == t2.hash_code() : ok
    • std::type_index(t1) == std::type_index(t2) : ok
    • &t1 == &t2 : not guaranteed
#include <iostream>
#include <typeinfo>
#include <typeindex>

class Animal 
{
public:
    virtual ~Animal() {}

};
class Dog : public Animal {};

void foo(Animal* p)
{
    const type_info& t1 = typeid(*p);
    const type_info& t2 = typeid(Dog);

    // 방법 1
    if (t1 == t2) // 실전 코드 : if( typeid(*p) == typeid(Dog))
    {
        std::cout << "p는 Dog" << std::endl;
    }
    // 방법 2
    if (t1.hash_code() == t2.hash_code())
    {

    }
    // 방법 3
    if (std::type_index(t1) == std::type_index(t2))
    {

    }

    std::cout << t1.name() << std::endl;
}

int main()
{
    Animal a; foo(&a);
    Dog d; foo(&d);
}

 

 

반응형

반응형

가상 함수의 원리

  • 가상 함수가 1개 이상 포함 되어 있으면 컴파일러가 객체 주소에 가상 함수 테이블 포인터가 추가함
  • 가상 함수 테이블은 Class 별로 생성되어 RTTI 정보를 포함한 모든 가상함수 주소를 제공
  • 상속 받은 클래스의 가상 함수 테이블은 기본적으로 기반 클래스의 가진 가상 함수 주소를 가르키지만 파생 클래스에서 override하여 구현한 함수에 대해서는 구현한 가상함수의 주소를 가르킴
  • 가상함수 갯수가 많으면 오버헤드가 있음

가상함수 호출 원리

 

class Animal
{
    int age;
public:
    virtual ~Animal() {}
    virtual void foo() {}
    virtual void goo() {}
};

class Dog : public Animal
{
    int color;
public:
    virtual ~Dog() {}
    virtual void foo() override {}
};

int main()
{
    Animal a1, a2;
    Dog d1, d2;
    
    Animal* p = &d1;    
    p->foo(); // 내부적으로 p->vtptr[?]() 호출
}
반응형

반응형

static binding

  • 컴파일러가 컴파일 시간에 함수 호출을 결정
  • 포인터 타입으로 함수 호출을 결정
  • 빠름, 하지만, 비 이성적임
  • 이른 바인딩( Early Binding )
  • C++의 non-virtual function

 

dynamic binding

  • 실행 시간에 함수 호출을 결정
  • 포인터가 가리키는 메모리를 조사후 결정
  • 느림. 하지만, 이성적
  • 늦은 바인딩( Late Binding )
  • java, C++의 virtual function
// Static binding
class Animal
{
public:
    void Cry() { std::cout << "A Cry" << std::endl; }
};

class Dog : public Animal
{
public:
    void Cry() { std::cout << "D Cry" << std::endl; }
};

int main()
{
    Animal a;
    Dog d;

    Animal* p = &d;
    p->Cry();   // C++ : A
                // JAVA : D
}
// Dynamic binding
class Animal
{
public:
    virtual void Cry() { std::cout << "A Cry" << std::endl; }
};

class Dog : public Animal
{
public:
    virtual void Cry() override { std::cout << "D Cry" << std::endl; }
};

int main()
{
    Animal a;
    Dog d;

    Animal* p = &d;
    p->Cry();   // C++ : A
                // JAVA : D
}
반응형

반응형
  • 강한 결합(Tightly coupling)
    • 하나의 클래스가 다른 클래스를 사용할 때 서로의 이름을 직접 사용하는 것
    • 교체 불가능하고 확장성이 없음
class Camera
{
public:
    void take()
    {
        std::cout << "task picture" << std::endl;
    }
};

class HDCamera
{
public:
    void take()
    {
        std::cout << "task picture" << std::endl;
    }
};

class People
{
public:
    void useCamera(Camera* p) { p->take(); } // People이 Camera를 직접 접근
    void useCamera(HDCamera* p) { p->take(); } // People이 HDCamera를 직접 접근
};

int main()
{
    People p;
    Camera c1;
    p.useCamera(&c1);

    HDCamera c2;
    p.useCamera(&c2);
}

약한 결합(Loosely coupling)

  • 하나의 클래스가 다른 클래스를 사용할 때 인터페이스(추상 클래스)를 사용하는 것
  • 교체 가능하고, 확장성이 높음
// 사람과 카메라사이의 규칙 설계
// 모든 카메라는 ICamera 인터페이스를 구현 해야 함
class ICamera
{
public:
    virtual void take() = 0;
};

class People
{
public:
    void useCamera(ICamera* p) { p->take(); } // People이 공통 인터페이스(ICamera)로 접근
};

// 모든 카메라는 규칙을 지켜야 한다
class Camera : public ICamera
{
public:
    virtual void take() override
    {
        std::cout << "task picture1" << std::endl;
    }
};

class HDCamera : public ICamera
{
public:
    virtual void take() override
    {
        std::cout << "task picture2" << std::endl;
    }
};

int main()
{
    People p;
    Camera c1;
    p.useCamera(&c1);

    HDCamera c2;
    p.useCamera(&c2);
}
반응형

+ Recent posts