1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
///////////////////////
///////C++ 집합관계//////
//////////////////////
 
두가지의 집합관계가 존재함
 - Composition
 - Aggregation
 
Composition 관계는 구성 요소들도 함께 소멸 함.
Aggregation 관계는 개별적으로 분리되어 독립적으로 활용(독립적으로 소멸됨).
 
class CNode {
    friend class CMyList;
 
public:
    explicit CNode(const char *pszName) {
        strcpy(m_szName,pszName);
    }
 
private:
    char m_szName[20];
    CNode *pNext = nullptr;
};
 
class CMyList {
 
public:
    CMyList() : m_HeadNode("Test Head") {}
    ~CMyList() {
        
        CNode *pNode = m_HeadNode.pNext;
        CNode *pDelete = nullptr;
 
        while(pNode) {
            
            pDelete = pNode;
            pNode   = pNode->pNext;
            delete pDelete;
        }
        
        m_HeadNode,pNext = nullptr;
    }
 
    void AddNewNode(const char *pszName) {
        
        CNode *pNode = new CNode(pszName);
 
        pNode->pNext = m_HeadNode.pNext;
        m_HeadNode.pNext = pNode;
    }
 
    void Print() {
 
        CNode *pNode = m_HeadNode.pNext;
    
        while(pNode) {
            cout << pNode->m_szName << endl;
            pNode = pNode->Next;    
        }
    }
 
private:
    CNode m_HeadNode;
};
 
class CMyUI {
public:
    CMyUI(CMyList &rList) : m_list(rList);
    int printMenu() {
        cout << "[1]Add\t" << "[2]Print\t" << "[0]Exit\n";
        cout.flush();
        int nInput = 0;
        cin >> nInput;        
 
        return nInpit;
    }
 
    void Run() {
        char szName[20];
        int nInput = 0;
 
        whild((nInput = printMenu()) > 0) {
            
            switch(nInput) {
                case 1:
                cout << "이름: ";
                cout.flush();
                cin >> szName;
                m_list.AddNewNode(szName); 
                break;            
                
                case 2:                
                m_list.print();
                break;
 
                default:
                break;
 
            }
        }
    }
 
private:
    //UI 클래스 내부에 자료구조가 포함된다.
    //전형적인 Composition관계
    CMyList m_list;
    
    //위 Composition관계를 Aggregation관계로 변경 하기 위해서는 포인터나 참조로 선언 해야 된다.
    //참조로 선언된 변수는 생성자에서 무조건 초기화를 해야 됨.
    CMyList &m_list;
};
 
int main(void) {
 
    CMyUI ui;
    ui.Run();    
 
    //참조로 선언을 했기 때문에 선언 형태도 변경됨//
    //CList 와 CMyUI의 객체를 별도로 선언하고 연결 했기 때문에 Aggregation 관계로 변경 된다.
    CList list;
    CMyUI ui2(list);
    ui2.Run();
    
 
    return 0;
}
 
참고 문헌) 이것이 C++이다

 
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/////////////
//대입 연산자//
////////////
 
class CCopyStudy {
public:
    CCopyStudy(int nParam) {
        m_pnData = new int;
 
        *m_pnData = nParam;
    }
    
    //얕은 복사의 오류를 범하지 않기 위해 값을 복사한 깊은 복사 형태//
    CCopyStudy(const CCopyStudy &rhs) {
 
        m_pnData = new int;
        
        *m_pnData = *rhs.m_pnData;
    }
 
    //단순대입 연산자를 사용했을시 얕은 복사 방지를 위한 코드 추가//
    CCopyStudy& operator=(const CCopyStudy &rhs) {
        
        *m_pnData = *rhs.m_pnData;
 
        return *this;
    }
    
    ~CCopyStudy() {
        delete m_pnData;
    }
 
    int GetData() {
        if(m_pnData != NULL) {
            return *m_pnData;
        }
        return 0;
    }
 
private:
    int *m_pnData = nullptr;
}
 
int main(void) {
    
    CCopyStudy a(10);
    
    //매개변수를 통한 복사//
    //얕은 복사를 방지 하기 위한 깊은 복사 형태를 주의 해야 됨//
    CCopyStudy b(a);
 
    //단순 대입 연산자를 사용 하게 될 경우 오류가 발생//
    //단순 대입 연산자는 얕은 복사를 사용하게 되어서 메모리 해제시 오류가 발생//
    //operator 단순 대입 연산자를 사용 해야 됨//
 
    CCopyStudy c(20);
    a = c;   //a.operator(c); //사용 방법이 2가지가 있음//
    
    
    
    return 0;
}
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
깊은 복사 
 - 복사에 의해 실제로 두 개의 값이 생성되는 것
 
얕은 복사 
 - 대상이 되는 값은 여전히 하나뿐인데 접근 포인터만 둘로 늘어나는 것
 
 
#include "stdafx.h"
#include <iostream>
using namespace std;
 
//얕은 복사 기본 정의(?)//
int _tmain(int argc, TCHAR* argv[]) {
    
    //대상이 하나
    int nScoreData = 10;
    
    int *pA, *pB;
    
    //하나의 대항을 향해 접근 포인터가 둘로 늘어남    
    pA = &nScoreData;
    pB = &nscoreData;
 
    return 0;
}
 
 
//////////////////
//얕은 복사의 문제점//
/////////////////
int _tmain(int argc, TCHAR* argv[]) {
        
    int *pA, *pB;
 
    pA = new int;
    *pA = 10;
    
    pB = new int;
    pB = pA; //얕은 복사의 표본(?)
 
    cout << *pA << endl;
    cout << *pB << endl;
    
    delete pA;
    delete pB;    
    
    return 0;
}
 
- 얕은 복사로 인하여 하나의 동적 생성된 인스턴스에 접근 포인터가 2개로 늘어 난 상태에서 
  메모리 해제를 해버리면 이미 해제가 된 상태인데 또 해제를 할려고 하니 에러가 발생됨
 
- 또한 pB가 생성한 인스턴스는 접근 불가능한 상태가 되버린다
 
- 그래서 포인터가 가리키는 주소를 변경 하는게 아니라 포인터가 가리키고 있는 메모리의 내용을 복사 해야 됨.
 
pB = pA; //얕은 복사의 표본(?)
이 형태를 변형 해야 한다.
 
*pB = *pA; //포인터가 가리키고 있는 메모리의 내용(값)을 복사
 
이렇게 복사를 하게 되면 메모리 해제를 하더라도 동적으로 생성된 모든(pA,pB) 인스턴스가 정상적으로 해제가 된다.
cs



캡슐화와 정보은닉의 개념이 헷갈릴수가 있다. 아래의 예를 통해서 캡슐화를 알아 보자


class Key

{

public:

void Key() 

{

cout<<"키를 꼽습니다."<<endl;;

}

};

class StartButton

{

public:

void Button()

{

cout<<"시동 버튼을 누릅니다."<<endl;

}

};

class Car

{

public:

void TakeKey(Key &key);

void TakeStartButton(StartButton &button);

};


int main(void)

{

Key key;

StartButton button;


Car car;

car.TakeKey(key);

car.TakeStartButton(button);


return 0;

}


위 코드는 간략하게 자동차 시동을 켜는 방법을 클래스다. 그러나 위 예제가 캡슐화가 무너진 대표적인 예다. 왜냐 하면 자동차는 항상 키,시동버튼 행동을 포함 하게 된다. 또한 눈으로 확인 가능 하기가 어렵다.


따라서 아래 코드 처럼 변경 하면 좋다.


class Key

{

public:

void Take() 

{

cout<<"키를 꼽습니다."<<endl;;

}

};

class StartButton

{

public:

void Take()

{

cout<<"시동 버튼을 누릅니다."<<endl;

}

};


class CarStart

{

private:

Key key;

StartButton btn;


public:

void Take()

{

key.Take();

btn.Take();

}

};


class Car

{

public:

void TakeCarStart(CarStart &start)

{

start. Take();

}

};

int main(void)

{

CarStart carStart;

Car car;

car. TakeCarStart(carStart);


return 0;

}


여기서 가장 중요한 것은 캡슐화의 범위를 결정 하는 일이 쉽지 않다는 것이다. 







먼저 어떠한 좌표를 저장 하는 클래스를 기본적으로 만들어 보자


class Point 

{

public:

int x;  //x 좌표는 0보다 커야 한다.

int y;  //y 좌표는 0보다 커야 한다.

}


int main(void)

{

Point p1 = {-2,4};

Point p2 = {3,5};


return 0;

}


위 코드를 보듯이 Point 멤버 변수 x,y를 public로 선언을 하는 바람에 어디서든 접근이 가능 하게 되어 버렸다. 따라서 0보다 작은 수를 실수로 저장이 되어 버리는 현상이 발견 되었다. 이 부분을 방지 하기 위해서 임의로 막아 주는 부분이 필요 하다.


Point.h 파일


class Point

{

private:

int x;

int y;


public:

      void InitMembers(int xpos,int ypos);

int GetX();

int GetY();


bool SetX(int xpos);

bool SetY(int ypos);

}


Point.cpp 파일


bool Point::InitMembers(int xpos,int ypos) 

{

if(xpos < 0 || ypos < 0)

{

return false;

}


x = xpos;

y = ypos;


return true;

}


int Point::GetX()

{

return x;

}

int Point::GetY()

{

return y;

}

bool Point::SetX(int xpos)

{

if(xpos < 0)

{

return false;

}

x = xpos;

return true;

}

bool Point::SetY(int ypos)

{

if(ypos < 0)

{

return false;

}

y = ypos;

return true;

}


위 코드 처럼 Point 멤버 변수 x,y를 숨겨서 개발자가 실수를 최대한 줄여 줄수 있게 하는 방법 입니다. 

또한 GetY(),GetX() 를 GetY() const ,GetX() const를 함으로써 더욱 안정성을 높여 주는 것도 하나의 방법이다.




스택과 힙의 큰 차이점은 동적, 정적의 차이라 볼 수 있겠다. 

동적 : 런타임시에 메모리 영역을 원하는 크기로 잡을 수 있다는 의미. 
정적 : 컴파일시에 크기가 정해져 있다는 의미. 

프로그래밍시 메모리 할당은 운영체제가 4개의 메모리에서 알아서 할당한다.
이를 단순화 시켜 하나의 공간으로 만든 것이 가상메모리 이다. 
코딩을 하면서 언급되는 메모리는 모두 가상메모리이다. 

가상메모리 영역 
* 코드 영역 - 코드가 저장됨
* 데이터 영역 - 전역변수, static 변수 처럼 프로그램 종료 될때까지 사라지지 않고 유지되는 는 정적인 영역 
* 스택 영역 지역변수, 매개변수 처럼 해당 영역을 빠져 나오면 소멸되는 영역
* 힙 영역 - 사용자가 실행중(런타임 중) 동적으로 생성, 소멸 시키는 영역 

 




이상입니다.



참고 : http://plming.tistory.com/52


include <iostream>

using namespace std;

class Animal;

class AnimalDelegate{
public:
    virtual void onSound(Animal* pSender)=0;
    virtual void onWalk(Animal* pSender)=0;
};

class Animal{
private:
    AnimalDelegate* delegate;

public:

    Animal(){
        this->delegate = NULL;
    }

    void setDelegate(AnimalDelegate* delegate){
        this->delegate = delegate;
    }

    void sound(){
        if(this->delegate){
            this->delegate->onSound(this);
        }
    }

    void walk(){
        if(this->delegate){
            this->delegate->onWalk(this);
        }
    }
};



//개 : Delegate를 내부에 직접 구현
class Dog:public Animal, public AnimalDelegate{
public:
    Dog(){
        this->setDelegate(this);
    }

    void onSound(Animal* pSender){
        cout << "Dog: 멍멍!" << endl;
    }

    void onWalk(Animal* pSender){
        cout << "Dog: 개가 걷는다" << endl;
    }
};

//고양이 Delegate
class CatDelegate:public AnimalDelegate{
public:
    void onSound(Animal* pSender){
        cout << "Cat: 야옹~" << endl;
    }

    void onWalk(Animal* pSender){
        cout << "Cat: 고양이가 걷는다" << endl;
    }
};

//고양이
class Cat:public Animal{};


void main(){

    Dog dog;
    Cat cat;

    dog.sound();
    dog.walk();
    
    CatDelegate cdel;

    cat.setDelegate(&cdel);
    cat.sound();
    cat.walk();
    
    getchar();
}

포인터 상수 , 상수 포인터 상당히 헷갈린다.


차근 차근 풀어써 이번에 꼭 기억 해야지.... 기억 보다는 이해가 좋을듯


포인터 상수


흔히 배열을 포인터 상수 라고 합니다. 


char test[] = "abcde";    test는 포인터 상수 라고 한다. 


위 배열의 선언에서 배열명은 test , 배열명은 배열 전체를 의미 하기도 하지만 

배열의 시작 주소 값을 나타 내기도 한다.

배열의 시작번지는 test[0]의 주소값 &test[0] 이다.

배열은 시작번지 부터 차례로 메모리상에서 연속적으로 붙어 있는 상태 인데 test의 주소가 변하게 되면 배열이 

엉망으로 이루어 진다. 

따라서 test의 주소가 변하지 않도록 현재 주소 값이 상수로 지정되어 있다.


캬.... 쉽다.... 이렇게 하니깐 쉽네.. 배열은 흔히 알수 있듯이 주소가 이어져 있다 근데 주소가 바뀌면 안되니깐 상수로 되어 있어야 한다. 따라서 배열은 포인터 상수다 . 포인터 상수는 주소값이 바꿀수 없도록 한 상태 이다.~!


단, 주소값은 고정 되어 있지만 주소값이 가리키는 대상체는 얼마든지 바꿀 수 있다..~!~! 명심 하도록 ~!~!




상수 포인터 


const는 변수를 상수화 해주는 키워드 이다. #define도 특정 문자열을 상수화 하지만 const와 다르게 전처리기에 의한 치환에 의해 코드에 삽입된다는 것이 다르다..


const char *test = "abcde";   여기서 test는 상수 포인터(상수지시포인터)


*포인터 상수는 주소값이 상수 이기 때문에 주소값이 변화가 안되지만 대상체는 

 변경이 가능 하고 (값을 변경)

 상수 포인터는 타입만 맞다면 다른 대상체로 가리킬수가 있다.


const char *test = "abcde";

test = "12345";


그런데 const 가 앞에 있을때가 있고 뒤에 있을때가 있다..~! (어렵다 ㅜㅜ)


const char *test =  "abcde";       // 상수 포인터

char *const test = "12345";      // 포인터 상수

const char *const test = "good"; // 상수지시포인터상수 (완전 변경 불가능)


배열이 포인터 상수 인 부분이 제일 중요 한것 같다..


외울려고 하지 말고 컴퓨터를 이해 하자~~


참고) http://thrillfighter.tistory.com/88





 















int iVal = 10;  // 전역 변수


int ScopeFunc() 

{

int iVal = 15; // 지역 변수

iVal += 10;   // 지역 변수

:: iVal += 10; // 전역 변수

}


신기 ~!~! 신기 ^^


#define 와 inline의 차이점


#define


장점 : 일반적인 함수에 비해 실행속도의 이점이 있다.

단점 : 정의하기가 어렵다. 복잡한 함수를 매크로의 형태로 정의하는데 한계가 있다.


#define SQUARE(x) ((x)*(x))


int main(void) {

std::cout<<SQUARE(5)<<std::endl;

return 0;

}


inline

장점 : 매크로 함수 지만 일반 함수 처럼 정의가 가능

단점 : 자료형에 따라 함수를 늘려야 한다.(다만,템플릿을 이용하면 제외)


inline int SQUARE(int x) {

return x*x;

}


int main(void) {

std::cout<<SQUARE(5)<<std::endl;

return 0;

}


템플릿을 이용한 inline

자료형에 구애 받지 않고 매크로 함수 구현 가능 하게 된다.


template <typename T>


inline  T SQUARE(T x) {

return x*x;

}


int main(void) {

std::cout<<SQUARE(5)<<std::endl;

return 0;

}


+ Recent posts