{{ label!='' ? 'Label : ' : (q!='' ? '검색 : ' : '전체 게시글') }} {{ label }} {{ q }} {{ ('('+(pubs|date:'yyyy-MM')+')') }}

[C++] 스터디 CPPALOM 2주차: 열혈 C++ 프로그래밍 Chap 3-4

(파워 포인트 파일(.pptx)을 Markdown으로 변환하여 업로드하였음)


# <br>2주차 - 열혈 C++ 프로그래밍 Chapter 3-4

# <br>구조체

* C언어에서의 구조체
  * “값의 집합”
* C++에서의 구조체 선언
* C++에서의 구조체 변수의 선언

```C++
struct Car
{
    char gamerID[ID_LEN];    
    int fuelGauge;      
    int curSpeed;
};
```

```C++
Car basicCar;
Car simpleCar;
```

* 자료형이란:
  * 값의 집합과 연산의 집합
* C언어에서는 값의 집합만 지원하므로, 다음과 같이 연산의 집합은 전역 함수로 선언해야 한다.
* 이는 error-prone한 구조이다.

```C++
void ShowCarState(Car &car)
{
    cout<<"소유자ID: "<<gamerID<<endl;
    cout<<"연료량: "<<fuelGauge<<"%"<<endl;
    cout<<"현재속도: "<<curSpeed<<"km/s"<<endl<<endl;
}
```

C++에서는 연산 역시 구조체에 선언할 수 있음

```C++
struct Car
{
    char gamerID[CAR_CONST::ID_LEN];    
    int fuelGauge;      
    int curSpeed;       
    void ShowCarState();
};
void Car::ShowCarState()
{
    cout<<“ 사용자ID: "<<gamerID<<endl;
    cout<<“ 연료량: "<<fuelGauge<<"%"<<endl;
    cout<<“ 현재속도: "<<curSpeed<<"km/s"<<endl<<endl;
}
```

# <br>클래스

* 클래스의 선언
  * struct 키워드를 class로 쓰면 된다.
* 클래스와 구조체의 차이:
  * 접근 지시자의 차이
  * 구조체는 기본이 public, 클래스는 기본이 private.

```C++
class Car
{
    char gamerID[CAR_CONST::ID_LEN];    
    int fuelGauge;      
    int curSpeed;       
    void ShowCarState();
};
void Car::ShowCarState()
{
    cout<<“ 사용자ID: "<<gamerID<<endl;
    cout<<“ 연료량: "<<fuelGauge<<"%"<<endl;
    cout<<“ 현재속도: "<<curSpeed<<"km/s"<<endl<<endl;
}
```

# <br>접근제어 지시자

```C++
class Car
{
private:
    char gamerID[CAR_CONST::ID_LEN];    
    int fuelGauge;      
    int curSpeed;       
public:
    void InitMembers(char * ID, int fuel);
    void ShowCarState();
    void Accel();
    void Break();
};
void Car::InitMembers(char * ID, int fuel)
{
    strcpy(gamerID, ID);
    fuelGauge=fuel;
    curSpeed=0;
};
...
int main(void)
{
    Car run99;
    run99.InitMembers("run99", 100);
    run99.Accel();
    run99.Accel();
    run99.Accel();
    run99.ShowCarState();
    run99.Break();
    run99.ShowCarState();
    return 0;
}
```

* public
  * 어디서든 접근 허용
* protected
  * 상속 관계에 놓여있을 때, 유도 클래스에서의 접근 허용
* private
  * 클래스 내에서만 접근 허용
* 일반적인 선언:
  * 세부 구현인 변수는 private으로
  * 인터페이스인 함수는 public으로

# <br>주의점

* private은 클래스에 대한 접근제어 지시자이다.
  * per-class basis이지, per-object basis가 아니다.
  * 서로 다른 객체이더라도, 같은 클래스라면 직접 접근할 수 있다!

```C++
class Vector2D
{
private:
    int x;
    int y;
public:
    Vector2D(int x, int y) : x(x), y(y) {}
    int getX();
    int getY();
    Vector2D plus(Vector2D other)
    {
        // return Vector2D(x + other.getX(), y + other.getY())
        return Vector2D(x + other.x, y + other.y);
    }
};
```

# <br>객체, 멤버변수, 멤버함수

* 클래스:
  * 설계도
* 객체:
  * 설계도를 통해서 생성된 실체(instance)
* 멤버변수:
  * 클래스 내의 변수
* 멤버함수:
  * 클래스 내의 함수

# <br>파일분할

* 클래스의 선언과 정의를 분리한다.
  * 이는 Java에서 interface의 쓰임과 유사하다.
  * 즉, 같은 헤더의 내용을 정의하더라도 서로 다른 방식으로 구현된 여러 .cpp 파일들이 충분히 존재할 수 있다.
    * 이들은 Linker를 통해서 선택될 수 있다.

# <br>파일분할 예시

```C++
<Car.h>
#ifndef __CAR_H__
#define __CAR_H__
namespace CAR_CONST
{
    enum
    {
        ID_LEN      =20,
        MAX_SPD     =200,
        FUEL_STEP   =2,
        ACC_STEP    =10,
        BRK_STEP    =10
    };
}
class Car
{
private:
    char gamerID[20];   
    int fuelGauge;      
    int curSpeed;       
public:
    void InitMembers(char * ID, int fuel);
    void ShowCarState();
    void Accel();
    void Break();
};
#endif
```

선언과 관련한 includ는 헤더 파일에, 구현과 관련한 include는 cpp 파일에!

```C++
<Car.cpp>
#include <iostream>
#include <cstring>
#include "Car.h"
using namespace std;
void Car::InitMembers(char * ID, int fuel)
{
    strcpy(gamerID, ID);
    fuelGauge=fuel;
    curSpeed=0;
};
void Car::Accel()
{
    if(fuelGauge<=0)
        return;
    else
        fuelGauge-=CAR_CONST::FUEL_STEP;
    if((curSpeed+CAR_CONST::ACC_STEP)>=CAR_CONST::MAX_SPD)
    {
        curSpeed=CAR_CONST::MAX_SPD;
        return;
    }
    curSpeed+=CAR_CONST::ACC_STEP;
}
...
```

# <br>객체 활용하기

* 생성
  * 일반적인 변수의 선언방식과 동적 할당방식이 있다.
* 함수 호출
  * <객체 이름>.<멤버함수 이름>

```C++
ClassName objName;
ClassName* ptrObj = new ClassName;
FruitSeller seller;
FruitBuyer buyer;
FruitSeller* objPtr1 = new FruitSeller;
FruitBuyer* objPtr = new FruitBuyer;
```

```C++
FruitSeller seller;
seller.InitMembers(1000, 20, 0);
FruitBuyer buyer;
buyer.InitMembers(5000);
buyer.BuyApples(seller, 2000);
```

# <br>정보은닉과 캡슐화

* Information Hiding
  * 필요한 정보만을 드러낸다.
* Encapsulation
  * 관련있는 값의 집합과 연산의 집합을 묶는다.
* 철학
  * 정보 은닉: API와 세부 구현을 분리하며, 클라이언트는 API만 의존한다.
  * 캡슐화: 특정한 행위를 할 때, 사용자는 “편리”해야 한다.
    * 세부적인 내용들을 클라이언트가 일일이 조작하는 것 대신, 거대한 블랙박스에서 클라이언트 입력과 출력만을 제어한다.
    * 이를 테면, Façade Pattern.

# <br>생성자와 소멸자

* 객체 생성 시 단 한 번만 호출되는 함수
* 특징:
  * 클래스의 이름과 함수의 이름이 동일하다.
  * 반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다.
  * 오버로딩이 가능하다.
  * 매개변수에 디폴트 값을 설정할  있다.

```C++
#include <iostream>
using namespace std;
class SimpleClass
{
    int num1;
    int num2;
public:
    SimpleClass()
    {
        num1=0;
        num2=0;
    }
    SimpleClass(int n)
    {
        num1=n;
        num2=0;
    }
    SimpleClass(int n1, int n2)
    {
        num1=n1;
        num2=n2;
    }
    void ShowData() const
    {
        cout<<num1<<' '<<num2<<endl;
    }
};
```

```C++
int main(void)
{
    SimpleClass sc1;
    sc1.ShowData();
    SimpleClass sc2(100);
    sc2.ShowData();
    SimpleClass sc3(100, 200);
    sc3.ShowData();
    return 0;
}
```

# <br>멤버 이니셜라이저

Rectangle 클래스는 Point 두 개를 지닌다.

멤버 초기자를 이용해 이를 손쉽게 초기화할 수 있다.

```C++
#ifndef __RECTANGLE_H_
#define __RECTANGLE_H_
#include "Point.h"
class Rectangle
{
    Point upLeft;
    Point lowRight;
public:
    Rectangle(const int &x1, const int &y1, const int &x2, const int &y2);
    void ShowRecInfo() const;
};
#endif
```

```C++
#include <iostream>
#include "Rectangle.h"
using namespace std;
Rectangle::Rectangle(const int &x1, const int &y1, const int &x2, const int &y2)
            :upLeft(x1, y1), lowRight(x2, y2)
{
    // empty
}
void Rectangle::ShowRecInfo() const
{
}
```

```C++
class AAA
{
public:
    AAA()
    {
        cout<<"empty object"<<endl;
    }
    void ShowYourName()
    {
        cout<<"I'm class AAA"<<endl;
    }
};
class BBB
{   
private:
    AAA &ref;
    const int #
public:
    BBB(AAA &r, const int &n)
        : ref(r), num(n)
    {    }    
    void ShowYourName()
    {
        ref.ShowYourName();
        cout<<"and"<<endl;
        cout<<"I ref num "<<num<<endl;
    }
};
```

* 멤버 이니셜라이저의 이점:
  * 초기화의 대상을 명확히 인식할 수 있다.
  * 성능에 약간의 이점이 있다.
* const와 참조자 역시 선언이 가능해진다!

```C++
int main(void)
{
    AAA obj1;
    BBB obj2(obj1, 20);
    obj2.ShowYourName();
    return 0;
}
```

# <br>디폴트 생성자

* 생성자는 반드시 최소 하나를 갖는다!
  * 만약 생성자를 선언하지 않으면, 디폴트 생성자가 삽입된다.
  * 디폴트 생성자는 인자를 받지 않으며, 내부적으로 아무런 일도 하지 않는 생성자이다.
* 다음 둘은 동일하다:
* 주의점:
  * malloc()을 이용해 생성하면 생성자는 호출되지 않는다.

```C++
class AAA
{
public:
    AAA()
    {    }
    void ShowYourName()
    {
        cout<<"I'm class AAA"<<endl;
    }
};
```

```C++
class AAA
{
public:
    void ShowYourName()
    {
        cout<<"I'm class AAA"<<endl;
    }
};
```

# <br>private 생성자

* 만약 클래스 내부에서 객체를 생성할 것이라면, private으로 선언하여도 된다.
* 참고:
  * Singleton Pattern

```C++
#include <iostream>
using namespace std;
class AAA
{
private:
    int num;
public:
    AAA() : num(0) {}
    AAA& CreateInitObj(int n) const
    {
        AAA * ptr=new AAA(n);
        return *ptr;
    }
    void ShowNum() const { cout<<num<<endl; }
private:
    AAA(int n) : num(n) {}
};
```

```C++
int main(void)
{
    AAA base;
    base.ShowNum();
    AAA &obj1=base.CreateInitObj(3);
    obj1.ShowNum();
    AAA &obj2=base.CreateInitObj(12);
    obj2.ShowNum();
    delete &obj1;
    delete &obj2;
    return 0;
}
```

# <br>소멸자

* 객체 소멸 시 반드시 호출되는 함수
* 특징:
  * 클래스의 이름 앞에 ‘~’가 붙은 형태의 이름을 갖는다.
  * 반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다.
  * 매개변수는 void형으로 선언되어야 하기 때문에 오버로딩도, 디폴트 값 설정도 불가능하다.
* 이 역시 선언하지 않을 시 자동으로 기본 소멸자가 삽입된다.

```C++
class AAA
{
public:
    AAA()
    {    }
    ~AAA()
    {    }
};
```

# <br>소멸자의 활용

* 소멸자를 이용해 객체 소멸 과정에서 처리해야 할 일들을 자동으로 처리할 수 있다.
* 첨언:
  * 따라서, 동적으로 객체를 할당하는 일만을 처리하는 클래스를 선언하고 생성자와 소멸자를 이용해 이를 편리하게 다룰 수 있다. (클래스 바깥의 사용자는 이를 신경 쓰지 않아도 됨)
  * 이러한 것들을 컨테이너 클래스라고 한다.
    * 가령, vector 클래스

```C++
#include <iostream>
#include <cstring>
using namespace std;
class Person
{
private:
    char * name;
    int age;
public:
    Person(char * myname, int myage)
    {
        int len=strlen(myname)+1;
        name=new char[len];
        strcpy(name, myname);
        age=myage;
    }
    void ShowPersonInfo() const
    {
        cout<<"�̸�: "<<name<<endl;
        cout<<"����: "<<age<<endl;
    }
    ~Person()
    {
        delete []name;
        cout<<"called destructor!"<<endl;
    }
};
```

```C++
int main(void)
{
    Person man1("Lee dong woo", 29);
    Person man2("Jang dong gun", 41);
    man1.ShowPersonInfo();
    man2.ShowPersonInfo();
    return 0;
}
```

# <br>객체 배열

```C++
class Person
{
private:
    char * name;
    int age;
public:
    Person(char * myname, int myage)
    {
        int len=strlen(myname)+1;
        name=new char[len];
        strcpy(name, myname);
        age=myage;
    }
    Person()
    {
        name=NULL;
        age=0;
        cout<<"called Person()"<<endl;
    }
    void SetPersonInfo(char * myname, int myage)
    {
        name=myname;
        age=myage;
    }
    void ShowPersonInfo() const
    {
        cout<<"이름: "<<name<<", ";
        cout<<"나이: "<<age<<endl;
    }   
    ~Person()
    {
        delete []name;
        cout<<"called destructor!"<<endl;
    }
};
```

일반적인 변수 선언과 같이 선언할 수 있다.

단, 기본 생성자만을 호출할 수 있다.

```C++
int main(void)
{
    Person parr[3];
    char namestr[100];
    char * strptr;
    int age;
    int len;
    for(int i=0; i<3; i++)
    {
        cout<<"이름: ";
        cin>>namestr;
        cout<<"나이: ";
        cin>>age;
        len=strlen(namestr)+1;
        strptr=new char[len];
        strcpy(strptr, namestr);
        parr[i].SetPersonInfo(strptr, age); 
    }
    parr[0].ShowPersonInfo();
    parr[1].ShowPersonInfo();
    parr[2].ShowPersonInfo();
    return 0;
}
```

# <br>객체 포인터 배열

```C++
class Person
{
private:
    char * name;
    int age;
public:
    Person(char * myname, int myage)
    {
        int len=strlen(myname)+1;
        name=new char[len];
        strcpy(name, myname);
        age=myage;
    }
    Person()
    {
        name=NULL;
        age=0;
        cout<<"called Person()"<<endl;
    }
    void SetPersonInfo(char * myname, int myage)
    {
        name=myname;
        age=myage;
    }
    void ShowPersonInfo() const
    {
        cout<<"이름: "<<name<<", ";
        cout<<"나이: "<<age<<endl;
    }   
    ~Person()
    {
        delete []name;
        cout<<"called destructor!"<<endl;
    }
};
```

배열을 선언하는 대신, 객체 포인터 배열을 선언해 기본생성자 호출 없이 초기화해줄 수 있다.

```C++
int main(void)
{
    Person * parr[3];
    char namestr[100];
    char * strptr;
    int age;
    int len;
    for(int i=0; i<3; i++)
    {
        cout<<"이름: ";
        cin>>namestr;
        cout<<"나이: ";
        cin>>age;
        parr[i]=new Person(namestr, age);
    }
    parr[0]->ShowPersonInfo();
    parr[1]->ShowPersonInfo();
    parr[2]->ShowPersonInfo();
    delete parr[0];
    delete parr[1];
    delete parr[2];
    return 0;
}
```

# <br>this 포인터

* 객체 자신의 주소 값을 의미하는 포인터
* 특히, 멤버변수를 참조할 때 유용하게 사용함.
* 가끔은 다음과 같은 엄격한 규칙을 적용하기도 함:
  * 멤버변수를 참조할 때에는 반드시 this 키워드를 붙여 참조한다.

```C++
#include <iostream>
#include <cstring>
using namespace std;
class SoSimple
{
    int num;
public:
    SoSimple(int n) : num(n)
    {
        cout<<"num="<<num<<", ";
        cout<<"address="<<this<<endl;
    }
    void ShowSimpleData()
    {
        cout<<num<<endl;
    }
    SoSimple * GetThisPointer()
    {
        return this;
    }
};
```

```C++
class SoSimple
{
    int num;
public:
    SoSimple(int n)
    {
        this->num = numn;
    }
};
```

# <br>Self-Reference의 반환

* 객체 자신을 참조할 수 있는 참조자를 반환하는 함수를 구성할 수 있다.
* 참고:
  * Chaining
  * Builder Pattern

```C++
#include <iostream>
using namespace std;
class SelfRef
{
private:
    int num;
public:
    SelfRef(int n) : num(n)
    {
        cout<<"SelfRef(int n)"<<endl;
    }
    SelfRef& Adder(int n)
    {
        num+=n;
        return *this;
    }
    SelfRef& ShowTwoNumber()
    {
        cout<<num<<endl;
        return *this;
    }
};
```

```C++
int main(void)
{
    SelfRef obj(3);
    SelfRef &ref=obj.Adder(2);
    obj.ShowTwoNumber();
    ref.ShowTwoNumber();
    ref.Adder(1).ShowTwoNumber().Adder(2).ShowTwoNumber();
    return 0;
}
```

댓글

이 블로그의 인기 게시물

[코딩의탑] 4층: 툰 쉐이딩

[코딩의탑] 3층: 바다 렌더링

[코딩의탑] 5층: 포탈(Portal), 더 나아가기