// 파워포인트 같은 프로그램을 만들고싶다.

// 1. prototype 디자인패턴. - 가상복사생성자

// 2. Template Method 디자인패턴( C++에서는NVI라고도불린다. )

 

// VC2005 에서는반환형을자기로가져도된다. 아래참고

//      virtual Shape* Clone() { return new Circle(*this); }

//      virtual Circle* Clone() { return new Circle(*this); }

 

class Shape

{

public:

        // 자신의 복사본을 만들어내는 가상함수-> 가상복사생성자(prototype 디자인패턴)

        virtual Shape* Clone() { return new Shape(*this); }

 

        // 비가상함수.. public 에가상함수를놓지말자.

        void Draw()

        {

               // 멀티스레드를대비해서동기화객체를얻는다.

               RealDraw();    // 내부구현에서 가상함수를 호출하자.

               //cout << "Shape::Draw" << endl;

               // 동기화 객체를 반납한다.

        }

protected:

        // 모든 가상함수는 protected에 놓자.

        virtual void RealDraw()

        {

               cout << "Shape::Draw" << endl;

        }

};

 

class Rectangle : public Shape

{

public:

        void RealDraw()        { cout << "Rectangle::Draw" << endl; }

 

        virtual Rectangle* Clone() { return new Rectangle(*this); }

};

 

class Circle : public Shape

{

public:

        void RealDraw()        { cout << "Circle::Draw" << endl; }

 

        virtual Circle* Clone() { return new Circle(*this); }

};

 

void main()

{

        Shape* buf[1000];      // 동종의 객체(모든종류의도형)을 보관 할 수 있다.

        int count = 0;

 

        int cmd;

        while ( 1 )

        {

               cin >> cmd;

               if ( cmd == 4 )

               {

                       cout << "몇번째 도형을 복사할까요? >> ";

                       int k;

                       cin >> k;

 

                       buf[count++] = buf[k]->Clone();

               }

               if ( cmd == 1 ) buf[count++] = new Rectangle;

               if ( cmd == 2 ) buf[count++] = new Circle;

               if ( cmd == 3 )

               {

                       for ( int i = 0; i < count; ++i )

                              buf[i]->Draw();       // 다형성을띠게된다.

               }

        }

}

 

Tag | ,

7.31(화) C++ - 가상함수 테이블

from Study/C++ 2007/08/02 14:46 view 26048

가상함수 테이블(vtbl) 과 가상함수 테이블 포인터(vptr)

- 대부분의 컴파일러는 가상함수의 기능을 구현하기 위해 가상함수 테이블과 가상함수 테이블

포인터를 사용한다. 가상함수를 선언했거나 상속받은 클래스에는 가상함수의 모든 주소를 가

지고 있는 배열(혹은 링크드 리스트)이 생기는데 이를 가상함수 테이블 이라고 한다.

또한, 해당 클래스의 객체를 생성하면 해당 객체에는 멤버 data외에 가상 함수 테이블을

가르키는 포인터가 추가된다. 이를 가상함수 테이블 포인터라고 한다.

결국 가상함수를 사용하게 되면 실행시간 다형성(Run-time Polymorphism)을 얻을 수 있는 반면,

1. 가상함수 테이블을 위한 메모리 할당
( 가상함수가 많거나 상속이 깊을 경우 메모리 사용량이 많아진다. )

2. 객체 생성시 vptr의 추가로 인한 객체의 크기 증가
( 작은 객체 일 경우 상당히 비효율적 )

3. 함수 호출시 함수 포인터에 의한 호출로 약간의 오버헤드 발생

4. Inline을 사용할 수 없으므로 속도가 훨씬 느려진다.

// 예제 1.

class A

{

public:

        virtual void foo() { cout << "A::foo" << endl; } // 1

};

 

class B

{

public:

        virtual void goo() { cout << "B:goo" << endl; } // 2

};

 

void main()

{

        A a;

        B* p = (B*)&a;

 

        p->goo();      // 1을 출력한다.

}


// 예제 2.
// 가상함수의 테이블을 이해했다면 이건 메모리 참조에러가 나온다.

class A

{

        int x;

public:

        void foo() { cout << "A::foo" << endl; }

};

 

class B

{

        int y;

public:

        virtual void goo() { cout << "B:goo" << endl; } 

};

 

void main()

{

        A a;

        B* p = (B*)&a;

 

        p->goo();      // error

}


// 예제 3. 

// p->foo() 에서 디폴트 값은 컴파일 시간에 결정된다.!!!
class
A

{

public:

        virtual void foo( int a = 10 )
        { cout << "A::foo("<< a << ")" << endl; }    // 1

};

 

class B : public A

{

public:

        virtual void foo( int a = 20 )
        { cout << "B:foo("<< a << ")" << endl; }    // 2

};

 

void main()

{

        A* p = new B;

       

        p->foo();    // 디폴트 매개변수는 부모에게서 받아오고 출력은 2로 한다. 

 

        delete p;

}


Dynamic Binding -> 실행 시 결정된다.

= late binding

// Dynamic_Binding (가상함수 호출시는 처음) 메모리에서 처음 가상함수를 읽어온다.

Static Binding -> 컴파일러가 컴파일시 결정.

= early binding

// Static_Binding 자기를 호출하는 놈을 부른다. 메모리가 아니라 타입을 읽어온다.


컴파일 타임에는 타입을 보므로 디폴트타입을 정할떄에는 컴파일시 실행되므로 디폴트 값은 타입으로 가지고 호출하는 것은 메모리 즉, 포인터로 정해진 함수를 읽어온다.!!!

 

 

Tag |

7.31(화) C++ - 가상함수 활용하기

from Study/C++ 2007/08/02 14:34 view 22261

// 라이브러리내의모든클래스를표준출력과연계해서출력해보고싶다.

#include <string>

 

// 가상함수가아닌것을가상함수처럼보이게하기.

class object

{

public:

 

        virtual string ToString() const

        {

               return string("object");

        }

 

        friend ostream& operator<<( ostream& os, const object& o )

        {

               return os << o.ToString();

        }

};

 

class Point : public object

{

public:

        virtual string ToString() const

        {

               return string("Point");

        }

};

 

void main()

{

        object* pp = new Point;

        cout << *pp << endl;

 

        object o;

        cout << o << endl;

 

        Point p;

        cout << p << endl;

}

Tag |

7.31(화) C++ - 상속관계 활용하기

from Study/C++ 2007/08/02 14:00 view 21317

// 1. 부모* = &자식 
// 2. 1.이 주로 어떻게 활용 되는가??

// 장점 : 동일한 기능 부여!!, 모든 type을 저장하는 컬렉션 제공.


class object

{

};


class
Animal

{

public:

        int age;

};

 

class Dog : public Animal{};

class Cat : public Animal{};

 

// DogCat의 객체를 받기 위해서는 오버로딩으로 두번 써주지만..

// 부모를 이용하면 간단해진다.

void NewYear( Dog* p )

{

        p->age++;

}

void NewYear( Cat* p )

{

        p->age++;

}

/////////////////////////////////////////////

//void NewYear( void* p ) // 모든 type을 받을 수 있지만 구분을 못한다.(?)

void NewYear( Animal* p ) //모든 동물의 객체를 처리 할 수 있다.

{

        p->age++;

}

 
void main()

{

        Dog d;

        NewYear( &d );

 

        Cat c;

        NewYear( &c );

 

        Dog* p1 = new Dog;

//      int* p2 = new Dog;            // error

        Animal* p3 = new Dog;  // ok.. 부모* = &자식

 

        Dog d2;

        Animal& r4 = d2;       // ok.

        Animal o = d2; // ok.  liskov Substitution 리스코프의치환법칙.

}


//////////////////////////////////////////////////////////////////////////

class object

{

};


class
list

{

        struct Node

        {

               object* data;

               Node*   next;

        };

public:

        void Add( object* ob)

        {

               cout << "list::Add()" << endl;

        }

};

class Car : public object

{

};

class People : public object

{

};


// Car
전용Collection 을설계하자.

// strong type Collection - Adapter 패턴을활용해서만든것.

class Carcollection

{

        list st;

public:

        void Add( object* p ) { st.Add( p ); }

};

 

void main()

{

        Carcollection st;

 

        Car c;

        st.Add( &c );          // ok. 어짜피object의자식이다.

 

        People p;

        st.Add( &p );          // ok. Peopleobject의자식이다.

}

 

Tag |

class Base

{

public:

        void foo() { cout << "Base::foo" << endl; }

};

 

class Derived : public Base

{

public:

        using Base::foo;       // 자식에서도부모의foo를호출하겠다. 1의에러를보완!!

        void foo(int a) { cout << "Derived::foo" << endl; }

};

 

void main()

{

        Derived d;

        //d.foo();     // 1. error 부모와자식간엔오버로딩을성립하지않는다.

        d.foo();

        d.foo(10);     // 2. ok..

}

 

 

Tag |

7.30(월) C++ - Small Object Allocator

from Study/C++ 2007/07/31 16:47 view 26871

#include <iostream>

using namespace std;

 

// Small Object Allocator

// More Effective C++ 항목8

// Modern C++ design 4- 작은 객체의 메모리 할당기 만들기

// 미리 메모리를 크게 잡아놓고(리스트) new/delete를 해준다.
// 메모리 할당과 소멸시간을 줄여준다.
// 메모리를 사용하지 않으면 하드디스크에 페이지 형태로 잡아놓기 때문에
// 메모리의 낭비를 걱정 하지 않아도 된다??
 

class Point

{

        int x;

 

        union

        {

               int y;

               Point* next;

        };

        static Point* FreeList;

 

public:

        Point( int a = 0, int b = 0 ) : x(a), y(b) {}

 

        void* operator new( size_t sz )

        {

               if( sz != sizeof(Point) )      // Point 객체 할당이 아니라면

                       return ::operator new( sz );  // 전역new를 사용하게 한다.

               if( FreeList == 0 )           // 최초 실행되는 경우

               {

                       // 전역operator new를 사용해서500개를 사용하자.

                       FreeList = (Point*)::operator new(sizeof(Point)*500);

 

                       // single linked list로 연결한다.

                       for( int i = 0; i < 499; ++i )

                              FreeList[i].next = &FreeList[i+1];

               }

               // 1번째 노드를 떼어서 리턴해 준다.

               Point* temp = FreeList;

               FreeList = FreeList->next;

 

               return temp;

        }

        // delete 시에도 아래함수가 호출된다.

        // delete는 오버로딩이 되지 않는다.
        //
, 멤버일때 아래 모양만 허용된다. 자기를 delete를 방지??

        void operator delete( void* p, size_t sz )

        {

               if ( p == 0 ) return;         //NULL 포인터인지를 확인해줘야 한다.

 

               if( sz != sizeof(Point) )

               {

                       ::operator delete(p);

                       return;

               }

 

               // List의 제일 앞부분에 놓는다.

                Point* temp = (Point*)p;

               temp->next = FreeList;

               FreeList = temp;

        }      

};

 

Point* Point::FreeList = 0;

 

 

void main()

{

        Point* p1 = new Point( 1, 1 );

        Point* p2 = new Point( 2, 2 );

        Point* p3 = new Point( 3, 3 );

 

        cout << p2 << endl;

        delete p2;

 

        Point* p4 = new Point(0, 0);

 

        cout << p4 << endl;    // p4 와 같은주소는 p1, p2, p3중 어느것 일까요?

 

        delete p1;

        delete p3;

 

        Point* p5 = 0;

        delete p5;                    //NULL 포인터도 delete 가능하다.

}

 

Tag |

7.30(월) C++ - 상속과 포함

from Study/C++ 2007/07/31 15:14 view 28041

// 상속과 static

class Parent

{

public:

        static int x;

};

 

int Parent::x = 0;

 

class Child : public Parent

{

};

 

void main()

{

        Parent::x = 10;

        Child::x  = 20; //될까??

 

        cout << Parent::x << endl; // 얼마?? 20 부모자식이모두공유.!!

}

 

// 상속과생성자

class Parent

{

public:

        Parent() {}      // 1

        Parent(int a) {} // 2

};

 

class Child : public Parent

{

public:

// 3 컴파일러가 Parent()를 자동으로 생성해서 부모디폴트를 호출한다.
       
Child() : Parent() {} 

        Child(int a) : Parent(a) {} // 4

};

 

void main()

{

        Child c(1);    // 1- 4 주의부모는항상default 생성자호출

}

 
 

// protected 생성자의미: 자신을만들수없지만자식은만들수있다.!!!!

class Animal

{

protected:

        Animal() {}

};

 

class Dog : public Animal

{

public:

        Dog() {}

};

 

void main()

{

        Animal a;

   // protected로 감싸주면 현실세계를 반영 할 수 있다. 동물은 만들지 못하지만 개는 만든다.
       
Dog    d;
}

 

 

// 접근변경자- 언제사용하는가? 자주나오지는않지만알아둘필요가있다.

class Parent

{

private:       int a;

protected:     int b;

public:        int c;

};

 

class Child : public Parent

{

        int a;  // 메모리에있지만접근불가.

        int b;

        int c;

};

 

 

// STLlinked list가 있다. - 그런데 stack이 없다고 가정

// Adapter Design Pattern : 임의의클래스의interface(함수이름)을변경해서

//                         다른클래스처럼보이게하는기법.

// private 상속: 구현을물려받지만interface는물려받지않겠다.

 

#include <list>

 

// 부모의 가상함수를 재정의 해 주고 싶을 때에는 상속을 통하여 한다.

class Stack : private list<int>

{

public:

    // 부모인list를재사용한다.

        void Push( int a )     { push_back(a);  }
       
void Pop()             { pop_back();  }      // 제거만한다.

        int  top()             { return back(); }   // 리턴만한다.

};

 

void main()

{

        Stack s;

        s.Push(10);

 

   // s.push_front(10);
   //
이 순간 Stack은 망가지게 된다. private 상속을 통해 막아버리자.

}

// 하지만  상속보다는 포함을 사용한 Adapter가 훨씬 좋은 기법이다.

class Stack

{

        list<int> st;

public:

        void Push( int a ) { st.push_back(a); }

};

 

Tag |

7.30(월) C++ - 함수객체, 주소연산자

from Study/C++ 2007/07/31 15:08 view 28308

#include <iostream>

using namespace std;

 

// 함수객체

// ()연산자를 재정의해서 함수처럼 동작하는 객체.

// 상태를 가지는 함수-> 함수 보다 훨씬 뛰어나다.

// 암시적인 inline을 갖는다.

// 함수보다 빠를 때가 있다. 일반 함수가 인자로 전달될 때 함수 포인터를 사용한다.
// (inline
을쓰지못한다.pointer이기 때문에.)

 

// 아래와 같은 경우는 지속적인 값을 가지지못한다. 매번값을넘겨줘야한다.

int plus( int a, int b, int ba )

{

        static int base = 0;

        base = ba;

        return a + b + base;

}

/////////////////////////////////////////////////////////////

 

class plus

{

        int base;

public:

        plus( int a = 0 ) : base(a) {}

 

        int operator()(int a, int b)

        {

               return a + b + base;

        }

};

 

void main()

{

        plus p(10);

 

        int s = p( 1, 2 );     // p.operator()(1,2)

 

        cout << s << endl;

}

 

// 컴파일러가만들어주는것들

 class Point

{

        int x, y;

 

public:

        void* operator&()

        {

               return this;

        }

 

        const void* operator&() const

        {

               return this;

        }

};

 

 

void main()

{

        const Point p;

 

        cout << &p << endl;;
        // p.operator&()
주소연산자도재정의가능하지만안하니못하다.!!

}

 

Tag |

7.30(월) C++ - finding memory leak

from Study/C++ 2007/07/31 14:32 view 1737997

// memory.cpp
#include
"memchk.h"

 

// finding memory leak

int main()

{

        int* p1 = new int;

        int* p2 = new int;

        int* p3 = new int;

 

        delete p2;

 

//      cout << __FILE__ << endl;     // 컴파일 하는 파일이름

//      cout << __LINE__ << endl;     // 컴파일 하는 LINE NO

 

        return 0;

}

 

//memchk.h

#include <iostream>

using std::cout;

using std::endl;       // .h 에서는 절대로 namespace를 통째로 열지마라!!(격언중하나)

 

#ifdef _DEBUG

 

struct MemInfo

{

        char name[256];        // 화일이름

        int  line;             // new를호출한line no

        void* addr;            // 할당한메모리주소

};

 

MemInfo mem[10000];  // 최대10000개의 메모리 할당을기록( linked list를 사용해도 됨 )

int count = 0;

 

void* operator new( size_t s, char* file, int line )

{

        void* p = malloc( s );

 

        // 배열에 할당정보를 기록한다.

        mem[count].addr = p;

        mem[count].line = line;

        strcpy( mem[count].name, file );

 

        ++count;

 

        return p;

}

 

void operator delete( void* p )

{

        for( int i = 0; i < count; i++ )

        {

               if( mem[i].addr == p )

               {

                       mem[i] = mem[count-1]; // i번째를 제거

                       --count;

                       break;

               }

        }

        // 배열에 없는 정보라면 에러처리를 하는 것도 좋은방법.

        free(p);

}

 

int MAIN();

 

void main()

{

        MAIN();

 

        if ( count == 0 )

        {

               cout << "NO Memory Leak" << endl;

               return;

        }

 

        cout << "Found " << count << " Memory Leak" << endl;

 

        for( int i = 0; i < count; ++i )

        {

               cout << "FILE : " << mem[i].name << endl;

               cout << "FILE : " << mem[i].addr << endl;

               cout << "FILE : " << mem[i].line << endl;

               cout << endl;

        }

}

 

 

// 꼭 외워 두세요. new에 인자를 더 보내주기 위한 좋은 방법

#define new new(__FILE__, __LINE__)

#define main MAIN

 

// C에 적용해보고 싶을때!!

#define malloc(x)      MyMAlloc( x, __FILE__, __LINE__ )

#define free(x)               MyFree(x)

 

#endif // _DEBUG

Tag | ,

7.30(월) C++ - new/delete 총정리

from Study/C++ 2007/07/31 13:07 view 26744

// 1. new의 동작방식  (A) operator new() 호출해서 메모리 할당.
//                 (B) A가 성공하면 생성자 호출.

 

class Test

{

public:

        Test()  { cout << "Test()" << endl; }

        ~Test() { cout << "~Test()" << endl; }

};

 

void main()

{

        Test* p = (Test*)operator new( sizeof(Test) );

        operator delete( p );

        //Test* p = new Test;

        //delete p;

}

 

// 2. operator new() 를재정의할수있다.

// 3. Array New

// 4. Member New

class Point

{

public:

        void* operator new( size_t s )

        {

               return malloc( s );

        }

};

 

Point* p = new Point;
 

void* operator new( size_t sz )

{

        cout << "operator new" << endl;

 

        return malloc( sz );

}

 

// Array New

void* operator new[]( size_t sz )

{

        cout << "operator new[]" << endl;

        return malloc( sz );

}

 

void main()

{

        int* p = new int;

        delete p;

 

        int* p2 = new int[10]; // Array new 호출 -> 없다면

        delete[] p2;

}

// 5. overloading new

// 다음 함수는 왜 만들었을까요? 메모리부터 잡고 객체를 생성하는 기법.

class Test

{

public:

        Test() { cout << "Test()" << endl; }

};

/* 전달된객체를반환해준다!!!

void* operator new( size_t s, void* p )

{

        return p;

}

*/

void main()

{

        Test* p = new Test; // operator new( size_t )로메모리할당후생성자호출

 

        // 객체를메모리에먼저잡고생성자를호출하는기법MapView( ?? )

        // 메모리는Cmalloc 의잡고나서객체를집어넣는다.

        new(p) Test;      // p를이미생성된메모리에반환!!

                          // operator new( size_t, void* ) 호출후생성자호출.

}


////////////////////////////////////////////////////////////////////////
// 6. nothorw
의 동작 방식!!!

// new : 실패시 예외전달( std::bad_alloc )

// new(nothrow) : 실패시 0


void
* operator new( size_t s )

{

        // 실패시예외발생

}

 

// 코드를설명적으로만들수있다.

class nothrow_t   // empty class - sizeof(nothrow_t) => 1

                  // overloading 함수를만들때사용할수있다. 설명적인코드가된다.

{

};

nothrow_t nothrow;

 

void* operator new( size_t s, nothrow_t )

{

        // 실패시0을리턴

}

 

// new의 새로운 방식

#define new new(nothrow)    // 예전코드의호환을위해서define 해준다.

void main()

{ 

        // VC2005 등의최신컴파일러는모두new 가실패시예외가나온다.

        int *p = 0;

        try

        {

               p = new int[1000];

               *p = 10;

               delete[] p;

        }

        catch( std::bad_alloc e )

        {

               cout << "메모리할당실패" << endl;

        }

///////////////////////////////////////////////////////////////

        int* p = new int[];

        if ( p == 0 )

        {

               cout << "메모리를할당할수없습니다." << endl;

        }

        else

        {

               *p = 10;       // 메모리사용

               delete[] p;

        }

}


/////////////////////////////////////////////////////////////////////
void
* operator new( size_t s )               // 1

{

        return malloc( s );

}

 

void* operator new( size_t s, char* p )      // 2

{

        cout << "new : " << p << endl;

        return malloc( s );

}

 

void main()

{

        int* p = new int;

 

        int* p2 = new ("AAA") int;

 

        delete p;

}

 

 

 

 

Tag |