'C++'에 해당되는 글 24건

  1. 2020.12.17 소켓 프로그래밍의 개요
  2. 2020.12.16 예외 처리
  3. 2020.12.16 STL 연관 컨테이너
  4. 2020.12.16 STL 시퀀스 컨테이너
  5. 2020.12.16 STL 컨테이너 어댑터
  6. 2020.12.16 스마트 포인터
  7. 2020.12.16 템플릿
  8. 2020.12.16 다형성 기법
  9. 2020.12.15 캡슐화 기법
  10. 2020.12.15 오버로딩

소켓(Socket)

- 전구나 형광등을 고정하는 도구이자 전기를 공급하기 위한 투입구 역할을 수행

- 컴퓨터 네트워크에서 소켓은 통신을 위한 종착지와도 같은 역할을 수행함

- 두 개의 컴퓨터가 통신을 할 때는 각 컴퓨터의 소켓을 통해서 데이터를 교환함

- 소켓 프로그래밍은 크게 TCP와 UDP 방식으로 나뉨

 

TCP(Transmission Control Protocol)

- TCP는 연결형 프로토콜

- UDP에 비해서 속도가 느리지만 신뢰성 있는 데이터의 송수신을 보장

- 인터넷 내에서 데이터를 주고받을 때는 경로 설정을 효과적으로 수행하기 위해 데이터를 여러 개의 패킷으로 나누어 송신

 

UDP(User Datagram Protocol)

- UDP는 비연결형 프로토콜

- TCP에 비해서 통신의 신뢰성이 부족하지만 빠른 데이터 송수신이 가능

- 데이터를 주고받기 전에 사전 통신 절차를 거치지 않음

 

서버 & 클라이언트 구조

- 가장 대표적인 네트워크 구성 모델

- 일반적으로 웹 서비스를 이용할 때 웹 브라우저는 [클라이언트]에 해당하며 웹 서버는 [서버]에 해당

- 모든 데이터의 관리 및 처리가 [서버]에 집중된다는 특징이 있음

 

소켓 프로그래밍

- 서버: socket > bind > listen > accept > send & recv > closesocket

- 클라이언트 : socket > connect > send & recv > closesocket

 

포트(Port)

- 특정한 프로그램이 통신을 하기 위해서는 포트 번호를 할당해야 함

- 포트 번호는 16비트로 65,536개를 사용할 수 있으며 1번부터 1024번 포트는 시스템 포트로 사용

- 특정한 서버 프로그램을 만들었다면 해당 서버 프로그램이 사용될 포트 번호를 1025번 이상으로 선택하여 미리 설정해야 함

 

포트 설정

- 서버 & 클라이언트 구조의 시스템을 구축할 때 서버 프로그램의 포트 번호는 명시적으로 할당해야 함

- 또한 클라이언트 프로그램은 서버의 IP 주소와 포트 번호를 모두 알고 있어야 함

- 서버와 클라이언트의 연결이 수립되면 클라이언트 컴퓨터에는 사용 중이지 않은 임의의 포트가 할당됨

'C++' 카테고리의 다른 글

C++ 문제 풀이  (0) 2021.01.11
소켓 프로그래밍 함수와 Winsock2  (0) 2020.12.17
예외 처리  (0) 2020.12.16
STL 연관 컨테이너  (0) 2020.12.16
STL 시퀀스 컨테이너  (0) 2020.12.16
Posted by khon98
,

예외 처리

C++ 2020. 12. 16. 23:22

예외(Exception)

- 프로그램이 동작하는 과정에서 발생하는 예상치 못한 오류를 의미

- 발생할 가능성이 높은 오류에 대해서 예외 처리를 할 수 있도록 해줌

 

예외 처리 구문

- try - catch 구문을 이용해서 예외 처리를 수행할 수 있도록 함

- try : 특정한 코드 블록에서 예외가 발생할 수 있음을 명시

- catch : 발생한 예외에 대해서 핸들러가 특정한 내용을 처리

- throw : try 구문에서 발생한 오류를 전달

 

* 예외 발생 예제

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

int main(void) {
    int a = 7, b = 0;
    cout << a / b << 'n'; // 나누기 연산은 0으로 나눌수 없음, 오류 발생
    system("pause");
    return 0;
}

 

* 예외 처리 예제

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

int main(void) {
    int a = 7, b = 0;
    try {
        if (b == 0) {
            throw "0으로 나눌 수 없음";
        }
        cout << a / b << 'n';
    } catch (const char* str) {
        cout << str << '\n';
    }
    system("pause");
}

 

* 클래스에서의 예외 처리

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

template <typename T>
class Data {
private:
    T data;
public:
    Data(T data) : data(data) {}
    T getData() { return data; }
    Data<T> operator /(const Data<T> &other) {
        if (other.data == 0) {
            throw 0;
        }
        return Data(data / other.data);
    }
};

int main(void) {
    try {
        Data<int> a(7);
        Data<int> b(0);
        Data<int> result = a / b;
        cout << result.getData() << '\n';
    } catch (int e) {
        if (e == 0) {
            cout << "0으로 나눌 수 없음.\n";
        }
    }
    system("pause");
}

'C++' 카테고리의 다른 글

소켓 프로그래밍 함수와 Winsock2  (0) 2020.12.17
소켓 프로그래밍의 개요  (0) 2020.12.17
STL 연관 컨테이너  (0) 2020.12.16
STL 시퀀스 컨테이너  (0) 2020.12.16
STL 컨테이너 어댑터  (0) 2020.12.16
Posted by khon98
,

STL 연관 컨테이너

C++ 2020. 12. 16. 23:00

STL 연관 컨테이너

- STL 연관 컨테이너 라이브러리는 매우 활용도가 높은 자료구조를 제공

- 연관 컨테이너는 키와 값 형태의 관련 있는 데이터를 쌍으로 저장하는 컨테이너

- 가장 많이 사용되는 시퀀스 컨테이너는 집합과 맵임

 

Set(집합), Multi Set(멀티 집합), Map(맵), Multi map(멀티 맵)

 

집합

- 저장하는 데이터를 키로 사용하는 연관 컨테이너 

- 저장된 위치에 데이터를 삽입한다는 점에서 검색 속도가 빠르다는 특징이 있음

- 기본적으로 키의 중복을 허용하지 않음

#include <iostream>
#include <string>
#include <stdio.h>
#include <set>

using namespace std;

int main(void) {
    int array[5] = { 2, 4, 6, 8, 10 };
    set<int> s(array, array + 5);
    set<int>::iterator iter = s.begin();
    for (; iter != s.end(); iter++) {
        cout << *iter << ' ';
    }
    cout << '\n';
    s.insert(1);
    s.insert(3);
    s.insert(5);
    iter = s.begin();
    for (; iter != s.end(); iter++) {
        cout << *iter << ' ';
    }
    cout << '\n';
    system("pause");
    return 0;
}

 

- 저장하는 데이터를 키와 값 쌍의 형태로 사용하는 연관 컨테이너

- 정렬된 위치에 데이터를 삽입한다는 점에서 검색 속도가 빠르다는 특징이 있음

- 기본적으로 키의 중복을 허용하지 않음

#include <iostream>
#include <string>
#include <stdio.h>
#include <map>

using namespace std;

int main(void) {
    map<string, int> m;
    m["khon"] = 1; m["ohn01"] = 2; m["hon02"] = 3;
    map<string, int>:: iterator iter = m.begin();
    for (; iter != m.end(); iter++) {
        cout << iter->first << ":" << iter->second << '\n';
    }
    m["on03"] = 4;
    cout << m["없는 사람"] << '\n';
    iter = m.begin();
    for (; iter != m.end(); iter++) {
        cout << iter->first << ":" << iter->second << '\n';
    }
    system("pause");
    return 0;
}

'C++' 카테고리의 다른 글

소켓 프로그래밍의 개요  (0) 2020.12.17
예외 처리  (0) 2020.12.16
STL 시퀀스 컨테이너  (0) 2020.12.16
STL 컨테이너 어댑터  (0) 2020.12.16
스마트 포인터  (0) 2020.12.16
Posted by khon98
,

STL 시퀀스 컨테이너

C++ 2020. 12. 16. 21:53

STL 시퀀스 컨테이너

- STL 시퀀스 컨테이너 라이브러리는 매우 활용도가 높은 자료구조를 제공

- 기존의 C언어를 이용하면 구현하기 까다로웠던 다양한 자료구조를 손쉽게 이용할 수 있음

- 가장 많이 사용되는 시퀀스 컨테이너는 벡터(Vector)와 덱(Deque)

 

Vector(벡터), Deque(덱), List(리스트), Forward List(순방향 리스트)

 

- 덱은 양 끝에서 데이터를 넣거나 뺄 수 있는 자료 구조

- PUSH_FRONT : 덱의 앞에 데이터를 삽입

- POP_FRONT : 덱의 앞에서 데이터를 꺼냄

- PUSH_BACK : 덱의 뒤에 데이터를 삽입

- POP_BACK : 덱의 뒤에서 데이터를 꺼냄

- INSERT : 덱의 특정 위치에 데이터를 삽입

#include <iostream>
#include <string>
#include <stdio.h>
#include <deque>

using namespace std;

int main(void) {
    deque<int> d;
    d.push_front(3); d.push_back(7); d.pop_front(); d.push_front(4);
    for (int i = 0; i < d.size(); i++) { cout << d[i] << ' '; }
    cout << '\n';
    deque<int>::iterator iter;
    iter = d.begin();
    d.insert(iter + 1, 3, 5);
    iter = d.begin();
    d.insert(iter + 1, 1, 9);
    for (int i = 0; i < d.size(); i++) { cout << d[i] << ' '; }
    cout << '\n';
    d.clear();
    cout << d.empty() << '\n';
    system("pause");
    return 0;
}

 

벡터

- 벡터는 뒤쪽에서만 데이터를 넣거나 뺄 수 있는 자료 구조

- 배열처럼 사용하기에 적합하다는 점에서 알고리즘 문제풀이에서 가장 많이 사용됨

- 실제로 문제풀이에서는 앞뒤로 원소가 추가되는 경우가 적으며 임의 원소에 접근해야 하는 경우가 많아 덱 자료구조와 비교했을 때 일반적인 경우 성능적으로 더 효율적임

- PUSH_BACK : 벡터의 뒤에 데이터를 삽입

- POP_BACK : 벡터의 뒤에서 데이터를 꺼냄

- INSERT : 벡터의 특정 위치에 데이터를 삽입

#include <iostream>
#include <string>
#include <stdio.h>
#include <vector>

using namespace std;

int main(void) {
    vector<int> v;
    v.push_back(3); v.push_back(5); v.push_back(8);
    vector<int>::iterator iter;
    iter = v.begin();
    v.insert(iter + 1, 3, 7);
    for (int i = 0; i < v.size(); i++) {
        cout << v[i] << ' ';
    }
    cout << '\n';
    v.clear();
    cout << v.empty();
    system("pause");
    return 0;
}

'C++' 카테고리의 다른 글

예외 처리  (0) 2020.12.16
STL 연관 컨테이너  (0) 2020.12.16
STL 컨테이너 어댑터  (0) 2020.12.16
스마트 포인터  (0) 2020.12.16
템플릿  (0) 2020.12.16
Posted by khon98
,

STL 컨테이너 어댑터

C++ 2020. 12. 16. 21:22

STL 컨테이너 어댑터

- STL 컨테이너 어댑터 라이브러리는 매우 활용도가 높은 자료구조를 제공

- 기존의 C언어를 이용하면 구현하기 까다로웠던 다양한 자료구조를 손쉽게 이용할 수 있음

 

Stack(스택), Queue(큐), Priority Queue(우선순위 큐)

 

STL 컨테이너 어댑터: 스택

- C++ Stack STL은 다음과 같은 함수로 구성

 

추가 : push(원소)

삭제 : pop()

조회 : top()

검사 : empty() / size()

 

#include <iostream>
#include <string>
#include <stdio.h>
#include <stack>

using namespace std;

int main(void) {
    stack<int> s;
    s.push(7); s.push(5); s.push(4); s.pop(); s.push(6); s.pop();
    while (!s.empty()) { // 스택에 원소가 하나라도 남아 있는 경우 실행
        cout << s.top() << ' ';
        s.pop();
    }
    system("pause");
}

 

STL 컨테이너 어댑터: 큐

- C++ Stack STL은 다음과 같은 함수로 구성

 

추가 : push(원소)

삭제 : pop()

조회 : front() / back()

검사 : empty() / size()

 

#include <iostream>
#include <string>
#include <stdio.h>
#include <queue>

using namespace std;

int main(void) {
    queue<int> q;
    q.push(7); q.push(5); q.push(4); q.pop(); q.push(6); q.pop();
    while (!q.empty()) {
        cout << q.front() << ' ';
        q.pop();
    }
    system("pause");
}

 

STL 컨테이너 어댑터: 우선순위 큐

- 큐 라이브러리에 포함

#include <iostream>
#include <string>
#include <stdio.h>
#include <queue>

using namespace std;

int main(void) {
    int n, x;
    cin >> n;
    priority_queue<int> pq;
    for (int i = 0; i < n; i++) { cin >> x; pq.push(x); }
    while (!pq.empty()) {
        cout << pq.top() << ' ';
        pq.pop();
    }
    system("pause");
}

'C++' 카테고리의 다른 글

STL 연관 컨테이너  (0) 2020.12.16
STL 시퀀스 컨테이너  (0) 2020.12.16
스마트 포인터  (0) 2020.12.16
템플릿  (0) 2020.12.16
다형성 기법  (0) 2020.12.16
Posted by khon98
,

스마트 포인터

C++ 2020. 12. 16. 21:05

스마트 포인터(Smart Pointer)

- 프로그래머의 실수로 메모리 누수를 방지하기 위한 수단으로 포인터처럼 동작하는 클래스 템플릿

- 기본적으로 힙 영역에 동적 할당된 메모리를 해제하기 위해서는 delete 키워드를 쓰면 됨

- 스마트 포인터를 이용하면 메모리 누수를 더 효과적으로 방지할 수 있기 때문에 컴퓨터 시스템의 안정성을 높일 수 있음

- 일반적으로 new 키워드를 이용해서 기본 포인터가 특정한 메모리 주소를 가리키도록 초기화 한 이후에 스마트 포인터에 해당 포인터를 넣어서 사용할 수 있음

- 정의된 스마트 포인터는 수명을 다했을 때 소멸자에서 delete 키워드를 이용해 할당된 메모리들을 자동으로 해제하는 기능을 수행함

- unique_ptr : 하나의 스마트 포인터가 특정한 객체를 처리할 수 있도록 함

- shared_ptr : 특정한 객체를 참조하는 스마트 포인터가 총 몇 개인지를 참조함

- weak_ptr : 하나 이상의 shared_ptr 인스턴스가 소유하는 객체에 대한 접근을 제공

 

unique_ptr

- 하나의 스마트 포인터만이 특정한 객체를 처리하도록 할 때 unique_ptr을 사용할 수 있음

- 스마트 포인터는 특정한 객체의 소유권을 가지고 있을 때만 소멸자가 객체를 삭제할 수 있음

 

* unique_ptr 소유권 이전과 메모리 할당 해제

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

int main(void) {
    unique_ptr<int> p1(new int(10));
    unique_ptr<int> p2;
    cout << "스마트 포인터 1: " << p1 << '\n';
    cout << "스마트 포인터 2: " << p2 << '\n';
    cout << "--- 소유권 이전 ---\n";
    p2 = move(p1); // 소유권 이전
    cout << "스마트 포인터 1: " << p1 << '\n';
    cout << "스마트 포인터 2: " << p2 << '\n';
    cout << "--- 메모리 할당 해제 ---\n";
    p2.reset(); // 메모리 할당 해제
    cout << "스마트 포인터 1: " << p1 << '\n';
    cout << "스마트 포인터 2: " << p2 << '\n';
    system("pause");
}

 

* unique_ptr 객체에 접근하기

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

int main(void) {
    unique_ptr<int> p1(new int(10));
    cout << *p1 << '\n'; // 관리하고 있는 객체를 반환함
    system("pause");
}

 

* 메모리 해제 이후에 객체에 접근

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

int main(void) {
    int* arr = new int[10];
    unique_ptr<int> p1(arr);
    for (int i = 0; i < 10; i++) {
        arr[i] = i;
    }
    for (int i = 0; i < 10; i++) {
        cout << arr[i] << ' ';
    }
    p1.reset();
    cout << '\n';
    for (int i = 0; i < 10; i++) {
        cout << arr[i] << ' ';
    }
    system("pause");
}

 

shared_ptr

- shared_ptrd은 하나의 특정한 객체를 참조하는 스마트 포인터의 개수가 몇 개인지를 참조

- 특정한 객체를 새로운 스마트 포인터가 참조할 때마다 참조 횟수가 1씩 증가하며 각 스마트 포인터의 수명이 다할 때마다 1씩 감소

- 결과적으로 참조 횟수가 0이 되면 delete 키워드를 이용해 메모리에서 데이터를 자동으로 할당 해제

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

int main(void) {
    int* arr = new int[10];
    arr[7] = 100;
    shared_ptr<int> p1(arr);
    cout << p1.use_count() << '\n'; // 1
    shared_ptr<int> p2(p1);
    cout << p1.use_count() << '\n'; // 2
    shared_ptr<int> p3 = p2;
    cout << p1.use_count() << '\n'; // 3
    p1.reset();
    p2.reset();
    cout << "arr[7]: " << arr[7] << '\n';
    p3.reset();
    cout << p1.use_count() << '\n';
    cout << "arr[7]: " << arr[7] << '\n';
    system("pause");
}

 

weak_ptr

- weak_ptr은 하나 이상의 shared_ptr 객체가 참조하고 있는 개체에 접근할 수 있음

- 해당 객체의 소유자의 수에는 포함되지 않는 스마트 포인터

- 일반적으로 서로가 상대방을 가리키는 두 개의 shared_ptr이 있다면 참조 횟수는 0이 될 수 없기 때문에 메모리에서 해제될 수 없음

- weak_ptr은 이러한 순환 참조 현상을 제거하기 위한 목적으로 사용할 수 있음

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

int main(void) {
    int* arr = new int(1);
    shared_ptr<int> sp1(arr);
    weak_ptr<int> wp = sp1; // wp는 참조 횟수 계산에서 제외
    cout << sp1.use_count() << '\n'; // 1로 통일
    cout << wp.use_count() << '\n';
    if (true) {
        shared_ptr<int> sp2 = wp.lock(); // shared_ptr 포인터 반환
        cout << sp1.use_count() << '\n';
        cout << wp.use_count() << '\n';
    }
    // 스코프를 벗어나므로 sp2가 해제됨
    cout << sp1.use_count() <<'\n';
    cout << wp.use_count() <<'\n';
    system("pause");
}

'C++' 카테고리의 다른 글

STL 시퀀스 컨테이너  (0) 2020.12.16
STL 컨테이너 어댑터  (0) 2020.12.16
템플릿  (0) 2020.12.16
다형성 기법  (0) 2020.12.16
캡슐화 기법  (0) 2020.12.15
Posted by khon98
,

템플릿

C++ 2020. 12. 16. 19:35

일반화

- 일반화 프로그래밍(Generic Programming)이 가능한 언어

- 템플릿을 이용해서 일반화 프로그래밍을 사용할 수 있음

 

템플릿(Template)

- 템플릿이란 매개 변수의 타입에 따라서 함수 및 클래스를 손쉽게 사용할 수 있도록 해줌

- 템플릿은 그 타입 자체가 매개 변수에 의해서 다루어짐

- 템플릿을 사용하면 타입마다 별도의 함수나 클래스를 만들지 않고 다양한 타입에서 동작할 수 있는 단 하나의 객체를 정의할 수 있음

- 결과적으로 소스코드의 재사용성을 극대화할 수 있는 객체 지향 프로그래밍 기법 중 하나

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

template <typename T>
void change(T& a, T& b) {
    T temp;
    temp = a;
    a = b;
    b = temp;
}


int main(void) {
    string a = "khon";
    string b = "khon01";
    change(a, b);
    cout << a << ":" << b << endl;
    system("pause");
    return 0;
}

 

함수 템플릿(Fuction Template)

- 각각의 자료형에 대해서 처음으로 호출이 될 때 C++ 컴파일러는 해당 타입의 인스턴스를 생성

- 이후에 생성된 하나의 인스턴스는 해당 자료형에 대해서 특수화가 이루어짐

- 이러한 인스턴스는 해당 함수 템플릿에 해당 자료형이 사용될 때마다 호출됨

 

명시적 특수화(Explicit Specialization)

- C++의 함수 템플릿은 특정한 타입에 대하여 명시적 특수화 기능을 제공

- 이러한 명시적 특수화를 이용하면 특정한 타입에 대해서 특수한 기능을 정의할 수 있음

- 컴파일러는 호출된 함수에 대응하는 특수화된 정의를 발견한 이후에는 해당 정의만을 사용

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

template <typename T> // int형이 아닌경우 수행
void change(T& a, T& b) {
    T temp;
    temp = a;
    a = b;
    b = temp;
}

template <> void change<int>(int& a, int& b) { // int형인 경우 수행
    cout << "정수형 데이터를 교체.\n";
    int temp;
    temp = a;
    a = b;
    b = temp;
}

int main(void) {
    int a = 8;
    int b = 7;
    change(a, b);
    cout << a << ":" << b << endl;
    system("pause");
    return 0;
}

 

클래스 템플릿(Class Template)

- 클래스를 일반화하기 위해서 클래스 템플릿을 활용할 수 있음

- 클래스 템플릿을 사용하면 자료형에 따라서 다르게 동작하는 클래스 집합을 만들 수 있음

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

template <typename T>
class Data {
private:
    T data;
public:
    Data(T data) : data(data) {}
    void setData(T data) { this -> data = data; }
    T getData() { return data; }
};

int main(void) {
    Data<int> data1(1);
    Data<string> data2("khon");
    cout << data1.getData() << ":" << data2.getData() << "\n";
    system("pause");
    return 0;
}

 

* 디폴트 템플릿 인수

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

template <typename T = int>
class Data {
private:
    T data;
public:
    Data(T data) : data(data) {}
    void setData(T data) { this -> data = data; }
    T getData() { return data; }
};

int main(void) {
    Data<> data1(1);
    Data<string> data2("khon");
    cout << data1.getData() << ":" << data2.getData() << "\n";
    system("pause");
    return 0;
}

'C++' 카테고리의 다른 글

STL 컨테이너 어댑터  (0) 2020.12.16
스마트 포인터  (0) 2020.12.16
다형성 기법  (0) 2020.12.16
캡슐화 기법  (0) 2020.12.15
오버로딩  (0) 2020.12.15
Posted by khon98
,

다형성 기법

C++ 2020. 12. 16. 17:33

다형성

- 다형성이란 여러 개의 서로 다른 객체가 동일한 기능을 서로 다른 방법으로 처리할 수 있는 기능을 의미

- 게임 프로그램, 칼, 대포, 총 등의 무기들은 공통적으로 공격이라는 동일한 기능을 수행할 수 있음

- 아래와 같은 구성에서는 무기 객체에서 attack() 함수를 실질적으로 구현할 필요가 없음

- 이럴 때 무기 객체를 추상 클래스로 구현하면 효과적으로 설계를 할 수 있음

 

무기 객체 : 칼, 대포, 총

 

자식 클래스에서 오버라이딩의 문제점

- 자식 클래스에서 멤버 함수를 재정의하여 사용하는 것은 일반적으로 정상적으로 동작함

- 포인터 변수로 객체에 접근할 때는 예상치 못한 결과가 발생

- C++ 컴파일러는 포인터 변수가 가리키고 있는 변수의 타입을 기준으로 함수를 호출하지 않고 포인터의 타입을 기준으로 함수를 호출

- 따라서 A라는 객체를 가리키는 포인터 변수는 A객체의 멤버 함수만을 호출할 수 있음

 

* 일반적인 함수의 정적 바인딩

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

class A {
public:
    void show() { cout << "A 클래스" << endl; }
};

class B : public A {
    void show() { cout << "B 클래스" << endl; }
};

int main(void) {
    A* p;
    A a;
    B b;
    p = &a; p->show();
    p = &b; p->show(); // 여전히 A 클래스의 show() 함수를 호출
    system("pause");
    return 0;
}

 

동적 바인딩

- C++은 특정한 함수를 호출할 때 해당 함수의 루틴이 기록된 메모리 주소를 알아야 함

- 특정한 함수를 호출하는 소스코드에서 실제로 함수가 정의된 메모리 공간을 찾기 위해서는 바인딩 과정이 필요

- 일반적으로 함수의 호출은 컴파일 시기에 고정된 메모리 주소를 이용함

- 이러한 방식을 정적 바인딩(Static Binding)이라고 하는데 C++의 일반적인 멤버 함수는 모두 이러한 정적 바인딩을 사용

- 가상 함수는 프로그램이 실행될 때 객체를 결정한다는 점에서 컴파일 시간에 객체를 특정할 수 없음

- 가상 함수는 실행 시간 때 올바른 함수가 실행될 수 있도록 동적 바인딩(Dynamic Binding)을 사용

 

가상 함수(Virtual Function)

- 가상 함수란 자식 클래스에서 재정의할 수 있는 멤버 함수

- vritual 키워드를 이용해 가상 함수를 선언할 수 있으며 자식 클래스에서 가상 함수를 재정의 하면 재정의된 멤버 함수 또한 가상 함수로 분류

- C++ 컴파일러는 가상 함수 테이블(Virtual Function Table)을 이용해 가상 함수를 다루게 됨

- 컴파일러는 각각의 객체마다 가상 함수 테이블을 가리키는 포인터를 저장하기 위한 멤버를 하나씩 저장함

- 가상 함수 테이블에는 특정한 클래스의 객체들을 위해 선언된 가상 함수들의 주소가 저장됨

- 가상 함수를 호출하면 C++ 프로그램은 가상 함수 테이블에 접근하여 자신이 필요한 함수의 주소를 찾아 호출하게 됨

- 이러한 과정은 말 그대로 동적 바인딩을 통해 이루어지므로 컴퓨팅 리소스를 소모하게 됨

- C++은 기본적으로 정적 바인딩을 채택하고 있음

- 자식 클래스가 재정의할 가능성이 있는 멤버 함수들은 가상 함수로 선언하는 것이 좋음

 

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

class A {
public:
    virtual void show() { cout << "A 클래스" << endl; } // 가상 함수
};

class B : public A {
    virtual void show() { cout << "B 클래스" << endl; } // 가상 함수
};

int main(void) {
    A* p;
    A a;
    B b;
    p = &a; p->show();
    p = &b; p->show();
    system("pause");
    return 0;
}

 

가상 클래스의 소멸자

- 상속 관계가 있으면서 그와 동시에 메모리 해제를 해야 하는 경우에는 반드시 부모 클래스의 소멸자를 가상 함수로 선언해야 함

- 부모 포인터로 객체를 삭제하면 부모 클래스의 소멸자가 호출되기 때문

- 만약 다형성을 이용할 때 소멸자를 가상 함수로 선언하지 않으면 자식 클래스의 소멸자는 호출되지 않고 부모 클래스의 소멸자만 호출되기 때문에 자식 클래스의 객체는 여전히 정상적으로 해제되지 않음

 

순수 가상 함수(Pure Virtual Function)

- 가상 함수는 기본적으로 반드시 재정의할 필요는 없음

- 순수 가상 함수는 자식 클래스에서 반드시 재정의를 해주어야 하는 함수

- 일반적으로 순수 가상 함수는 부모 클래스에서 함수 동작의 본체를 정의하지 않음

- 자식 클래스에서 반드시 이를 정의해야 사용할 수 있음

- 순수 가상 함수는 =0 키워드를 붙여 선언할 수 있음

 

* 순수 가상 함수는 함수 프로토타입 정의만 있고, 함수 바디 즉 코딩은 없어야 함

함수 바디는 상속 받아서 만들어야 함

=0; 는 순수 가상 함수를 정의하므로, 뒤에 { .. } 로 함수 바디 코딩을 할 수 없음

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

class A {
public:
    virtual void show()=0
};

class B : public A {
public:
    virtual void show() { cout << "B 클래스" << endl; } // show() 함수를 재정의 하지 않으면 B 클래스의 객체를 사용할 수 없음
};

int main(void) {
    A* p;
    B b;
    p = &b; p->show();
    system("pause");
    return 0;
}

 

추상 클래스(Abstract Class)

- 추상 클래스란 하나 이상의 순수 가상 함수를 포함하는 클래스를 의미

- 추상 클래스를 활용하면 다형성을 효과적으로 프로그램 상에서 구현할 수 있음

- 따라서 자식 클래스는 추상 클래스를 상속받은 이후에 반드시 순수 가상 함수를 모두 오버라이딩 해야 비로소 해당 객체를 사용할 수 있음

'C++' 카테고리의 다른 글

스마트 포인터  (0) 2020.12.16
템플릿  (0) 2020.12.16
캡슐화 기법  (0) 2020.12.15
오버로딩  (0) 2020.12.15
클래스 상속  (0) 2020.12.15
Posted by khon98
,

캡슐화 기법

C++ 2020. 12. 15. 23:22

프렌드

- 기본적으로 멤버 변수에 접근하기 위해서 public 멤버 함수를 이용해야 함

- 특정한 객체의 멤버 함수가 아닌 경우에도 private 멤버에 접근해야 할 때가 있음

- 이때 프렌드 키워드를 이용하면 특정한 객체의 모든 멤버에 접근할 수 있음

 

캡슐화

- 캡슐화란 객체 지향 프로그래밍 기법에서 중요한 키워드

- 캡슐화의 기본 원리는 관련된 함수 및 멤버들은 되도록 하나의 클래스에서 관리하는 것

- 같이 실행되어야 하는 기능들도 하나의 클래스에 넣어서 각 객체가 응집된 기능을 가질 수 있도록 해야 함

 

프렌드 함수

- 기존의 함수 앞에 friend 키워드를 붙인 형태로 사용할 수 있음

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

class Student {
private:
    int studentId;
    string name;
public:
    Student(int studentId, string name) : studentId(studentId), name(name) {}
    friend Student operator + (const Student &student, const Student &other) {
        return Student(student.studentId, student.name + " & " + other.name); // friend 키워드를 이용해 이름에 접근
    }
    void showName() { cout << "이름: " << name << '\n'; }
};

int main(void) {
    Student student(1, "khon");
    Student result = student + student;
    result.showName();
    system("pause");
    return 0;
}

 

프렌드 클래스

- 프렌드 멤버 함수 이외에도 프렌드 클래스 형태로 사용할 수 있음

- 두 클래스가 서로 밀접한 연관성이 있으며 상대방의 private 멤버에 접근해야 한다면 클래스 자체를 프렌드로 선언할 수 있음

- 프렌드 클래스에서는 모든 멤버 함수가 특정 클래스의 프렌드임

 

* 프렌드 클래스: 시간 클래스 정의하기

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <stdio.h>
#include <ctime>

using namespace std;

class Time {
    friend class Date; // Date 클래스에서 Time 클래스를 이용할 수 있음
private:
    int hour, min, sec;
public:
    void setCurrentTime() {
        time_t currentTime = time(NULL);
        struct tm *p = localtime(&currentTime);
        hour = p->tm_hour;
        min = p->tm_min;
        sec = p->tm_sec;
    }
};

class Date {
private:
    int year, month, day;
public:
    Date(int year, int month, int day) : year(year), month(month), day(day) {}
    void show(const Time &t) {
        cout << "지정된 날짜: " << year << "년 " << month << "월 " << day << "일 " << '\n';
        cout << "현재 시간: " << t.hour << ":" << t.min << ":" << t.sec << ":" << '\n';
    }
};

int main(void) {
    Time time;
    time.setCurrentTime();
    Date date = Date(2020, 12, 15);
    date.show(time);
    system("pause");
    return 0;
}

 

정적 멤버

- 클래스에는 포함되어 있는 멤버이지만 모든 객체가 공유하는 멤버

- 정적으로 선언된 멤버는 메모리 상에 오직 하나만 할당되어 관리됨

- 정적 멤버를 public으로 선언하면 외부의 어떠한 클래스에서 접근이 가능하며 오직 하나만 관리됨

- 정적 멤버는 일반적으로 싱글톤 패턴 등의 다양한 기능을 위해 사용됨

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

class Person {
private:
    string name;
public:
    static int count;
    Person(string name) : name(name) {
        count++;
    }
};

int Person::count = 0; // 정적 변수 초기화

int main(void) {
    Person p1("khon");
    Person p2("khon02");
    Person p3("khon01");
    cout << "사람의 수: " << Person::count << '\n';
    system("pause");
    return 0;
}

 

상수 멤버

- 호출된 객체의 데이터를 변경할 수 없는 멤버를 의미

- 일반적으로 클래스에서 사용되는 중요한 상수는 상수 멤버 변수로 정의해서 사용하는 관행이 있음

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

class Person {
private:
    const int id;
    string name;
public:
    static int count;
    Person(int id, string name) : id(id), name(name) {
        count++;
    }
    /*
     void setId(int id) {
        this->id = id; // 오류 발생 수정 불가능
     */
};

int Person::count = 0;

int main(void) {
    Person p1(1, "khon");
    Person p2(2, "khon02");
    Person p3(3, "khon01");
    cout << "사람의 수: " << Person::count << '\n';
    system("pause");
    return 0;
}

 

 

- C++에서 캡슐화를 위해 사용되는 프렌드, 정적 멤버 등의 개념을 바르게 숙지해야 객체 지향 프로그래밍 기법을 보다 자유롭게 활용할 수 있음

'C++' 카테고리의 다른 글

템플릿  (0) 2020.12.16
다형성 기법  (0) 2020.12.16
오버로딩  (0) 2020.12.15
클래스 상속  (0) 2020.12.15
생성자와 소멸자  (0) 2020.12.15
Posted by khon98
,

오버로딩

C++ 2020. 12. 15. 17:48

함수 오버로딩

- 동일한 이름의 멤버 함수를 다양한 방식으로 활용하기 위해서 오버로딩을 사용할 수 있음

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

class Person {
private:
    string name;
public:
    Person() { name = "임꺽정"; } // 매개 변수가 없을때는 멤버 변수인 name의 값으로 임꺽정을 넣음
    Person(string name): name(name) {} // 그렇지 않고 매개 변수가 존재하는 경우 그 이름 값으로 그대로 초기화를 진행
    void showName() { cout << "이름: " << name << '\n'; }
};

int main(void) {
    Person person1;
    person1.showName();
    Person person2("khon");
    person2.showName();
    system("pause");
    return 0;
}

 

연산자 오버로딩

- 연산자 오버로딩 문법을 활용해 연산자 또한 원하는 방식으로 수정하여 사용할 수 있다는 특징이 있음

- 기존에 존재하는 연산자만 정의할 수 있음(+ - * /)

- 멤버 연산자(.), 범위 지정 연산자(::) 등의 몇몇 연산자는 오버로딩 처리할 수 없음

- 피연산자의 개수 규칙 등 기본적인 연산자의 규칙을 따라야 함

- 오버로딩이 된 연산자의 피연산자 중 하나는 사용자 정의 자료형이어야만 함

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;

class Person {
private:
    string name;
public:
    Person() { name = "임꺽정"; }
    Person(string name): name(name) {}
    Person operator +(const Person& other) { return Person(name + " & " + other.name); }
    void showName() { cout << "이름: " << name << '\n'; }
};

int main(void) {
    Person person1;
    Person person2("khon");
    Person result = person1 + person2;
    result.showName();
    system("pause");
    return 0;
}

 

 

- 함수 오버로딩을 통해서 동일한 이름의 함수를 약간씩 변형하여 사용할 수 있음

- C++에서는 자주 이루어지는 특정한 계산을 연산자 오버로딩을 통해서 정리할 수 있음

'C++' 카테고리의 다른 글

다형성 기법  (0) 2020.12.16
캡슐화 기법  (0) 2020.12.15
클래스 상속  (0) 2020.12.15
생성자와 소멸자  (0) 2020.12.15
C++의 클래스  (0) 2020.12.14
Posted by khon98
,