14 분 소요

정보 은닉

정보 은닉의 이해

#include <iostream>

class Point
{
public:
    int x;
    int y;
};

class Rectangle
{
public:
    Point upLeft;
    Point lowRight;

public:
    void ShowRecInfo()
    {
        std::cout << "좌 상단: " << '[' << upLeft.x << ", ";
        std::cout << upLeft.y << ']' << std::endl;
        std::cout << "우 하단: " << '[' << lowRight.x << ", ";
        std::cout << lowRight.y << ']' << std::endl;
    }
};

int main()
{
    Point pos1 = {-2, 4};
    Point pos2 = {5, 9};
    Rectangle rec = {pos2, pos1};
    rec.ShowRecInfo();
    return 0;
}

Reactangle이라는 클래스 안에 Point 클래스 2개가 있어서 정보를 출력해주는 코드이다.


근데 이 코드에는 문제가 있다. 좌표의 범위를 제한하고 싶거나 정수임을 알려주고 싶은데, 그 부분이 해결이 되지 않았다.


sol!

// Point.h
#ifndef __POINT_H_
#define __POINT_H_

class Point
{
    private:
        int x;
        int y;
    public:
        bool InitMembers(int xpos, int ypos);
        int GetX() const;
        int GetY() const;
        bool SetX(int xpos);
        bool SetY(int ypos);
};

#endif

먼저 멤버변수 x와 y를 private로 선언해서 임의로 갑싱 저장되는 것을 막아놓았다. 즉 x와 y의 정보를 은닉한 상황이다. 대신에 값을 저장 및 참조하기 위한 함수를 추가로 정의 하였다.


그리고 InitMembers, SetX, SetY의 함수에서 값의 범위를 설정할 수 있다. 즉, 멤버변수를 private로 선언하고, 해당 변수에 접근하는 함수를 별도로 정의해서, 안전한 형태로 멤버 변수의 접근을 유도하는 것이 바로 정보 은닉이며 이는 좋은 클래스가 되기 위한 기본 조건이 된다!

#include <iostream>
#include "Point.h"

bool Point::InitMembers(int xpos, int ypos)
{
    if (xpos < 0 || ypos < 0)
    {
        std::cout << "벗어난 범위의 값 전달" << std::endl;
        return false;
    }

    x = xpos;
    y = ypos;
    return true;
}

int Point::GetX() const // const 함수!
{
    return x;
}

int Point::GetY() const
{
    return y;
}

bool Point::SetX(int xpos)
{
    if (0 > xpos || xpos > 100)
    {
        std::cout << "벗어난 범위의 값 전달" << std::endl;
        return false;
    }

    x = xpos;
    return true;
}

bool Point::SetY(int ypos)
{
    if (0 > ypos || ypos > 100)
    {
        std::cout << "벗어난 범위의 값 전달" << std::endl;
        return false;
    }
    y = ypos;
    return true;
}


이런 식으로 직사각형 클래스에 대한 .h 파일과 .cpp 파일도 만들어 준다.

// Rectangle.h
#ifndef __RECTANGLE_H_
#define __RECTANGLE_H_

#include "Point.h"

class Rectangle
{
private:
    Point upLeft;
    Point lowRight;

public:
    bool InitMembers(const Point &ul, const Point &lr);
    void ShowRecInfo() const;
};

#endif
// Rectangle.cpp
#include <iostream>
#include "Rectangle.h"

bool Rectangle::InitMembers(const Point &ul, const Point &lr)
{
    if (ul.GetX() > lr.GetX() || ul.GetY() > lr.GetY())
    {
        std::cout << "잘못된 위치 정보 전달" << std::endl;
        return false;
    }
    upLeft = ul;
    lowRight = lr;
    return true;
}

void Rectangle::ShowRecInfo() const
{
    std::cout << "좌 상단: " << '[' << upLeft.GetX() << ", ";
    std::cout << upLeft.GetY() << ']' << std::endl;
    std::cout << "우 하단: " << '[' << lowRight.GetX() << ", ";
    std::cout << lowRight.GetY() << ']' << std::endl
              << std::endl;
}
// RectangleFine.cpp
#include <iostream>
#include "Point.h"
#include "Rectangle.h"

int main()
{
    Point pos1;
    if (!pos1.InitMembers(-2, 4))
        std::cout << "초기화 실패" << std::endl;
    if (!pos1.InitMembers(2, 4))
        std::cout << "초기화 실패" << std::endl;

    Point pos2;
    if (!pos2.InitMembers(5, 9))
        std::cout << "초기화 실패" << std::endl;

    Rectangle rec;
    if (!rec.InitMembers(pos2, pos1))
        std::cout << "직사각형 초기화 실패" << std::endl;

    rec.ShowRecInfo();
    return 0;
}

>>>
벗어난 범위의  전달
초기화 실패
잘못된 위치 정보 전달
직사각형 초기화 실패
 상단: [1986518224, 1221022598]
 하단: [-2, 6422280]

예제 출력이랑 다르긴한데,, 이미 직사각형 자체가 정의되지 않았으므로 쓰레기 값이 들어가는 것이라고 생각한다. …

여튼 이 파일을 실행시키기 위해서는 Point 라는 폴더를 최상위로 빼주었고, g++ Point.cpp Rectangle.cpp RectangleFind.cpp -o filename./filename 를 터미널에 입력시켜주어서 출력했다.

const 함수

const 함수란

  1. 이 함수 내에서의 멤버 젼수에 저장된 값을 변경하지 않는다는 의미이다.
  2. const 함수내에서 값이 변경되지 않더라도 변경이 가능한 함수를 부르면 컴파일 에러가 가능하다. 즉, const 함수는 const 함수만 호출이 가능하다.

캡슐화

캡슐화 X 사례

#include <iostream>

class SinivelCap
{
public:
    void Take() const { std::cout << "콧물이 싹 납니다. " << std::endl; }
};

class SneezeCap
{
public:
    void Take() const
    {
        std::cout << "재채기가 멎습니다. " << std::endl;
    }
};

class SnuffleCap
{
public:
    void Take() const
    {
        std::cout << "코가 뻥 뚫립니다. " << std::endl;
    }
};

class ColdPatient
{
public:
    void TakeSinivelCap(const SinivelCap &cap) const { cap.Take(); }
    void TakeSneezeCap(const SneezeCap &cap) const { cap.Take(); }
    void TakeSnuffleCap(const SnuffleCap &cap) const { cap.Take(); }
};

int main()
{
    SinivelCap scap;
    SneezeCap zcap;
    SnuffleCap ncap;

    ColdPatient sufferer;
    sufferer.TakeSinivelCap(scap);
    sufferer.TakeSneezeCap(zcap);
    sufferer.TakeSnuffleCap(ncap);
    return 0;
}

>>>
콧물이  납니다.
재채기가 멎습니다.
코가  뚫립니다.

위 코드는 캡슐화가 이루어지지 않은 코드라고 볼 수 있다. 왜? 증상이 있다면 scap, zcap, ncap 약을 모두 복용해야되기 때문이다.

캡슐화 O 사례

#include <iostream>

class SinivelCap
{
public:
    void Take() const { std::cout << "콧물이 싹 납니다. " << std::endl; }
};

class SneezeCap
{
public:
    void Take() const
    {
        std::cout << "재채기가 멎습니다. " << std::endl;
    }
};

class SnuffleCap
{
public:
    void Take() const
    {
        std::cout << "코가 뻥 뚫립니다. " << std::endl;
    }
};

class CONTAC600
{
private:
    SinivelCap sin;
    SneezeCap sne;
    SnuffleCap snu;

public:
    void Take() const
    {
        sin.Take();
        sne.Take();
        snu.Take();
    }
};

class ColdPatient
{
public:
    void TakeCONTAC600(const CONTAC600 &cap) const { cap.Take(); }
};

int main()
{
    CONTAC600 cap;
    ColdPatient sufferer;
    sufferer.TakeCONTAC600(cap);
    return 0;
}

>>>
콧물이  납니다.
재채기가 멎습니다.
코가  뚫립니다.

위 코드는 캡슐화가 되었다고 볼 수 있다. 왜? cap이라는 캡슐약 하나만 먹으면 되기 때문이다.

결론적으로 같은 결과값이더라도 약을 3개를 먹냐, 하나에 해결을 하냐의 차이가 캡슐화의 의미라고 볼 수 있다.

그렇다면 모든 기능을 다 넣어서 캡슐화를 진행할 수 있지 않을까? 그게 더 효율적인 약 기능이 되지 않을까 라는 생각이 드는데 만약 CONTAC600에 기침, 몸살, 두통 기능까지 다 넣어버린다면 굳이 몸살만 난 환자는 굳이 CONTAC600을 먹을 필요는 없다.

이처럼 캡슐화의 범위를 정확히 결정하는 것은 중요하고, 정확한 결정을 하기 위해서는 다양한 사례가 필요하다.

정보 은닉 VS 캡슐화

  • 정보 은닉: public, private을 통해서 접근을 제한할 수 있다.
  • 캡슐화: 변수나 클래스를 감싸는 개념

캡슐화를 하게 되면 멤버 변수가 모이지 않게 정보를 은익해야된다. 그래서 캡슐화는 기본적으로 정보은닉을 포함하는 개념이라고도 한다.

생성자와 소멸자

생성자의 이해

생성자는 자바와 마찬가지고 클래스 이름과 동일한 형태를 띄우며 오버 로딩이 가능하며 디폴트 값 설정이 가능하다.

#include <iostream>

class SimpleClass
{
private:
    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;
    }
    /*
    SimpleClass(int n1 = 0, int n2 = 0)
    {
        num1 = n1;
        num2 = n2;
    }
    */

    void ShowData() const
    {
        std::cout << num1 << ' ' << num2 << std::endl;
    }
};

int main()
{
    SimpleClass sc1;
    sc1.ShowData();

    SimpleClass sc2(100);
    sc2.ShowData();

    SimpleClass sc3(100, 200);
    sc3.ShowData();

    return 0;
}

위 예시에서 보이는 것처럼 오버로딩이 가능하며, 디폴트 값 설정을 통해서 생성자를 만들 수 있다.

또한 생성자에서 매개변수가 정해져 있지 않다면 SimpleClass sc1; 와 같이 생성자를 호출해야 된다.

#include <iostream>

class SimpleClass
{
private:
    int num1;
    int num2;

public:
    SimpleClass(int n1 = 0, int n2 = 0)
    {
        num1 = n1;
        num2 = n2;
    }
    void ShowData() const
    {
        std::cout << num1 << ' ' << num2 << std::endl;
    }
};

int main()
{
    SimpleClass sc1();
    SimpleClass mysc = sc1();
    mysc.ShowData();
    return 0;
}

SimpleClass sc1()
{
    SimpleClass sc(20, 30);
    return sc;
}

>>> 20 30

이 예시가 뜻하는 바는 sc1이라는 생성자와 SimpleClass 타입을 가지고 있는 sc1() 함수 두 개를 만들었을 때 mysc는 어떤 것을 불러올까에 대한 해답이다.

출력문을 보면 sc1() 함수를 mysc 변수에 해당됨을 알 수 있다. 결국 컴파일러는 객체 생성문인지 원형 선언(함수 선언)인지를 구분할 수 없어서 함수의 원형 선언을 하기로 약속했다.

#include <iostream>

class FruitSeller
{
private:
    int APPLE_PRICE;
    int numOfApples;
    int myMoney;

public:
    FruitSeller(int price, int num, int money)
    {
        APPLE_PRICE = price;
        numOfApples = num;
        myMoney = money;
    }
    int SaleApples(int money)
    {
        int num = money / APPLE_PRICE;
        numOfApples -= num;
        myMoney += money;
        return num;
    }
    void ShowSalesResult() const
    {
        std::cout << "남은 사과: " << numOfApples << std::endl;
        std::cout << "판매 수익: " << myMoney << std::endl
                  << std::endl;
    }
};

class FruitBuyer
{
private:
    int myMoney;
    int numOfApples;

public:
    FruitBuyer(int money)
    {
        myMoney = money;
        numOfApples = 0;
    }
    void BuyApples(FruitSeller &seller, int money)
    {
        numOfApples += seller.SaleApples(money);
        myMoney -= money;
    }
    void ShowBuyResult() const
    {
        std::cout << "현재 잔액: " << myMoney << std::endl;
        std::cout << "사과 개수: " << numOfApples << std::endl
                  << std::endl;
    }
};

int main()
{
    FruitSeller seller(1000, 20, 0);
    FruitBuyer buyer(5000);
    buyer.BuyApples(seller, 2000);

    std::cout << "과일 판매자의 현황" << std::endl;
    seller.ShowSalesResult();
    std::cout << "과일 구매자의 현황" << std::endl;
    buyer.ShowBuyResult();
    return 0;
}

chap 3에서 만들었던 FruitSaleSim1.cpp 을 생성자를 활용해서 수정할 수 있다. chap 3에서는 InitMembers 라는 public 함수를 사용해서 private 값을 초기화시켰지만 이번 장에서는 생성자를 활용해서 private 값들을 초기화 시킨것을 확인할 수 있다.

멤버 이니셜라이져를 이용한 멤버 초기화

직사각형 만들던 코드에서는 생성자를 활용할 수 없다. 왜? chap 3 같은 경우는 각 클래스 마다 InitMembers 메소드를 넣어놓았기 때문에 순서대로 Point pos1, Point pos2를 생성하고, Rectangle 객체를 생성할 수 있었다.

하지만 생성자를 넣는 경우, 기존의 private 값들을 초기화 하는 역할을 하기 때문에 한번에 Rectangle 클래스를 만들 수 있을 것 같다. (뭐,, Rectangle rec(1, 1, 5, 5); 이런 형식으로? )

근데 저런 식으로 생성자를 생성하기 위해서는 Point 객체도 생성이 우선이 되어야 한다. 즉, Rectangle 객체를 생성하는 과정에서 Point 객체 2개가 생성되어야 되는 상황이다. 이럴 경우 멤버 이니셜라이져 를 사용한다.

// Point.h: Point 객체 멤버 선언
#ifndef __POINT_H_
#define __POINT_H_

class Point
{
private:
    int x;
    int y;

public:
    Point(const int &xpos, const int &ypos);
    int GetX() const;
    int GetY() const;
    bool SetX(int xpos);
    bool SetY(int ypos);
};

#endif
// Point.cpp: Point 객체 멤버 정의
#include <iostream>
#include "Point.h"

Point::Point(const int &xpos, const int &ypos)
{
    x = xpos;
    y = ypos;
}

int Point::GetX() const { return x; }
int Point::GetY() const { return y; }

bool Point::SetX(int xpos)
{
    if (0 > xpos || xpos > 100)
    {
        std::cout << "벗어난 범위의 값 전달 " << std::endl;
        return false;
    }
    x = xpos;
    return true;
}

bool Point::SetY(int ypos)
{
    if (0 > ypos || ypos > 100)
    {
        std::cout << "벗어난 범위의 값 전달" << std::endl;
        return false;
    }
    y = ypos;
    return true;
}

Point.h + Point.cpp = 범위에 맞게 좌표를 입력 받게끔 만든 Point 객체

// Rectangle.h: Rectangle 멤버 변수 선언
#ifndef __RECTANGLE_H_
#define __RECTANGLE_H_

#include "Point.h"

class Rectangle
{
private:
    Point upLeft;
    Point lowRight;

public:
    Rectangle(const int &x1, const int &y1, const int &x2, const int &y2);
    void ShowRectInfo() const;
};

#endif
// Rectangle.cpp: Rectangle 멤버 변수 정의
#include <iostream>
#include "Rectangle.h"

Rectangle::Rectangle(const int &x1, const int &y1, const int &x2, const int &y2) : upLeft(x1, y1), lowRight(x2, y2)

{
    // empty
}

void Rectangle::ShowRectInfo() const
{
    std::cout << "좌 상단: " << '[' << upLeft.GetX() << ", ";
    std::cout << upLeft.GetY() << ']' << std::endl;
    std::cout << "우 하단: " << '[' << lowRight.GetX() << ", ";
    std::cout << lowRight.GetY() << ']' << std::endl
              << std::endl;
}

Rectangle::Rectangle(const int &x1, const int &y1, const int &x2, const int &y2) : upLeft(x1, y1), lowRight(x2, y2)

이와 같이 객체 안에 객체가 들어가 있는 경우에 : 를 통해서 생성해야되는 객체의 생성자를 호출해준다.

#include <iostream>
#include "Point.h"
#include "Rectangle.h"

int main()
{
    Rectangle rec(1, 1, 5, 5);
    rec.ShowRectInfo();
    return 0;
}

>>>
 상단: [1, 1]
 하단: [5, 5]
  • cd Point2 -> g++ Point.cpp Rectangle.cpp RectangleConstructor.cpp -o filename
  • ./filename
  • output Result

멤버 이니셜라이저를 이용한 변수 및 const 상수 초기화

변수 및 const 역시 멤버 이니셜라이저로 초기화할 수 있는데, 사용 방법은 SoSimple(int n1, int n2) : num1(n1) 과 같다. 이렇게 된다면 num1이라는 private 변수를 n1로 초기화하는 꼴이 된다.

이렇게 사용하면 좋은 점

  • 초기화의 대상을 명확히 인식할 수 있다.
  • 성능에 약간의 이점이 있다. 성능에 이점이 있다는 말은 멤버 이니셜라이져를 통해서 초기화하는 경우는 int num1 = n1 과 같이 선언과 동시에 초기화하는 바이너리 코드가 구성되지만 n2 같은 경우는 num2 = n2 와 같이 초기화가 되는 바이너리 코드가 구성되므로 성능상 조금 차이가 이싿.


여튼 일반적으로는 멤버 변수의 초기화를 선호하는 편이라고 한다.

#include <iostream>

class FruitSeller
{
private:
    const int APPLE_PRICE;
    int numOfApples;
    int myMoney;

public:
    FruitSeller(int price, int num, int money) : APPLE_PRICE(price), numOfApples(num), myMoney(money)
    {
    }
    int SaleApples(int money)
    {
        int num = money / APPLE_PRICE;
        numOfApples -= num;
        myMoney += money;
        return num;
    }
    void ShowSalesResult() const
    {
        std::cout << "남은 사과: " << numOfApples << std::endl;
        std::cout << "판매 수익: " << myMoney << std::endl
                  << std::endl;
    }
};

class FruitBuyer
{
private:
    int myMoney;
    int numOfApples;

public:
    FruitBuyer(int money) : myMoney(money), numOfApples(0)
    {
    }
    void BuyApples(FruitSeller &seller, int money)
    {
        numOfApples += seller.SaleApples(money);
        myMoney -= money;
    }
    void ShowBuyResult() const
    {
        std::cout << "현재 잔액: " << myMoney << std::endl;
        std::cout << "사과 개수: " << numOfApples << std::endl
                  << std::endl;
    }
};

int main()
{
    FruitSeller seller(1000, 20, 0);
    FruitBuyer buyer(5000);
    buyer.BuyApples(seller, 2000);

    std::cout << "과일 판매자의 현황" << std::endl;
    seller.ShowSalesResult();
    std::cout << "과일 구매자의 현황" << std::endl;
    buyer.ShowBuyResult();
    return 0;
}

>>>
과일 판매자의 현황
남은 사과: 18
판매 수익: 2000

과일 구매자의 현황
현재 잔액: 3000
사과 개수: 2

멤버 변수로 참조자를 선언할 수 있다.

#include <iostream>

class AAA
{
public:
    AAA()
    {
        std::cout << "empty object" << std::endl;
    }
    void ShowYourName()
    {
        std::cout << "Im class AAA" << std::endl;
    }
};

class BBB
{
private:
    AAA &ref;
    const int &num;

public:
    BBB(AAA &r, const int &n) : ref(r), num(n)
    {
    }
    void ShowYourName()
    {
        ref.ShowYourName();
        std::cout << "and" << std::endl;
        std::cout << "I ref num " << num << std::endl;
    }
};

int main()
{
    AAA obj1;
    BBB obj2(obj1, 20);
    obj2.ShowYourName();
}

>>>
empty object
Im class AAA
and
I ref num 20

디폴트 생성자

객체가 되기 위해서는 반드시 하나의 생성자가 호출되어야 하는데, 이대 생성자를 정의하지 않는 클래스에서는 디폴트 생성자가 자동으로 삽입된다.

생성자가 하나도 정의되어 있지 않을 때만 생성된다.

private 생성자

앞에 예시 코드들은 모두 public 생성자로 선언되었다. 하지만 클래스 내부에서만 객체의 생성을 허용하려는 목적으로 생성자를 priate으로 선언하기도 한다.

#include <iostream>

class AAA
{
private:
    int num;

public:
    AAA() : num(0) {}
    AAA &CreateInitObj(int n) const
    {
        AAA *ptr = new AAA(n);
        return *ptr;
    }
    void ShowNum() const { std::cout << num << std::endl; }

private:
    AAA(int n) : num(n) {}
};

int main()
{
    AAA base;
    base.ShowNum();

    AAA &obj1 = base.CreateInitObj(3);
    obj1.ShowNum();

    AAA &obj2 = base.CreateInitObj(12);
    obj2.ShowNum();

    delete &obj1;
    delete &obj2;
    return 0;
}

public 생성자를 통해서 기본 숫자는 0으로 초기화를 해두었다. private 생성자를 사용하기 위해서는 public에도 생성자를 작성해두어서 외부 클래스에서도 사용할 수 있도록 해야된다. 뭐 여튼,, CreateInitObj 메소드를 통해서 클래스 내부에서 생성자를 만들어서 초기화 변수를 선헌한 객체를 obj1, obj2 에 전달해준다.

소멸자의 이해와 활용

  • 클래스의 이름 앞에 ~ 가 붙은 형태의 이름을 갖는다.
  • 반환형이 선언되어 있지 않으며, 실제로 반환하지 않느다.
  • 매개변수는 void 형으로 선언되어야 하기 때문에 오버 로딩도, 디폴트 값 설정도 불가능 하다.
#include <iostream>
#include <cstring>

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
    {
        std::cout << "이름: " << name << std::endl;
        std::cout << "나이: " << age << std::endl;
    }
    ~Person()
    {
        delete[] name;
        std::cout << "called destructor!" << std::endl;
    }
};

int main()
{
    Person man1("Lee dong woo", 29);
    Person woman1("Sohn soo kyoung", 22);
    man1.ShowPersonInfo();
    woman1.ShowPersonInfo();
    return 0;
}

>>>
이름: Lee dong woo
나이: 29
이름: Sohn soo kyoung
나이: 22
called destructor!
called destructor!

클래스와 배열 그리고 this 포인터

객체 배열

#include <iostream>
#include <cstring>

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;
        std::cout << "called Person()" << std::endl;
    }
    void SetPersonInfo(char *myname, int myage)
    {
        name = myname;
        age = myage;
    }
    void ShowPersonInfo() const
    {
        std::cout << "이름: " << name << std::endl;
        std::cout << "나이: " << age << std::endl;
    }
    ~Person()
    {
        delete[] name;
        std::cout << "called destructor!" << std::endl;
    }
};

int main()
{
    Person parr[3];
    char namestr[100];
    char *strptr;
    int age;
    int len;

    for (int i = 0; i < 3; i++)
    {
        std::cout << "이름: ";
        std::cin >> namestr;
        std::cout << "나이: ";
        std::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;
}

>>>
called Person()
called Person()
called Person()
이름: sk
나이: 22
이름: min
나이: 21
이름: jin
나이: 22
이름: sk
나이: 22
이름: min
나이: 21
이름: jin
나이: 22
called destructor!
called destructor!
called destructor!

this 포인터의 이해

객체 자기 자신을 가리키는 용도로 사용되는 포인터이다.

#include <iostream>
#include <cstring>

class SoSimple
{
private:
    int num;

public:
    SoSimple(int n) : num(n)
    {
        std::cout << "num = " << num << ", ";
        std::cout << "address = " << this << std::endl;
    }
    void ShowSimpleData()
    {
        std::cout << num << std::endl;
    }
    SoSimple *GetThisPointer()
    {
        return this;
    }
};

int main()
{
    SoSimple sim1(100);
    SoSimple *ptr1 = sim1.GetThisPointer();
    std::cout << ptr1 << ", ";
    ptr1->ShowSimpleData();
}

>>>
num = 100, address = 0x61ff08
0x61ff08, 100

this 는 자바와 같이 자기 자신의 멤버 변수를 초기화 시킬 때 사용한다.

Self-Reference의 반환

객체 자신을 참조할 수 있는 참조자를 의미한다.

#include <iostream>

class SelfRef
{
private:
    int num;

public:
    SelfRef(int n) : num(n)
    {
        std::cout << "객체 생성" << std::endl;
    }
    SelfRef &Adder(int n)
    {
        num += n;
        return *this;
    }
    SelfRef &ShowTwoNumber()
    {
        std::cout << num << std::endl;
        return *this;
    }
};

int main()
{
    SelfRef obj(3);
    SelfRef &ref = obj.Adder(2);

    obj.ShowTwoNumber();
    ref.ShowTwoNumber();

    ref.Adder(1).ShowTwoNumber().Adder(2).ShowTwoNumber();
    return 0;
}

>>>
5
5
6
8

뭐,, 이런식으로 사용하던 객체를 이어서 사용하거나 main 에서 클래스의 멤버 변수의 전 값을 유지하면서 사용하고 싶을 때 사용한다.

위 코드에서는 num을 사용할 수 잇는 참조 값을 ref 로 전달하여서 ref 변수도 num을 참조할 수 있게 되었으므로 값이 += 가 적용이 된것이다.

댓글남기기