7.31(화) C++ - 추상클래스

from Study/C++ 2007/08/02 15:46 view 19067

// 순수 가상함수 와 추상클래스

// 의미: 자식에게 반드시 특정함수를 만들게 하는것!

// Abstract Base Class 추상기반클래스

 

// 강한결합 tightly compling 값에 의한 전달!

// 약한결합 loosely compling 인터페이스에 의한 결합


//
사람과 전화기 제조업자가 지켜야 하는 계약서를 먼저 만든다. ( interface, contract )
// 인터페이스 설계의 중요성 : 확장성, 변화에 유연해 진다.
//
구현 부분이 없기 때문에 메모리를 잡지 않는다.

#define interface struct      //왠지 의미전달이 제대로 된다.멋있음.!!!

interface IPhone

{

        virtual void Calling( char* num ) = 0;

};

class IMP3Play

{

public:

        virtual void MP3Play() = 0;

};

 

//---------------------------------------------------------------

// 계약에 따른 전화기를 사용하는 객체

class People

{

public:

        void UsePhone( IPhone* p )    { p->Calling("119"); }

};

 

// 모든 전화기는 IPhone 인터페이스를 구현해야(상속 받아서 순수가상함수 재정의) 한다.

class AnyCall : public IPhone

{

public:

        void Calling( char* ) {}

};

 

void main()

{

        People p;

        AnyCall a;

 

        p.UsePhone( &a );

}

 

Tag |

7.31(화) C++ - typeid, type_info

from Study/C++ 2007/08/02 15:29 view 19919

#include <iostream>

#include <typeinfo.h>
using
namespace std;
 

// template에서instatiation type을알아보는방법

// const T& char const[6] 그러므로문자열의길이가다르다면에러!!

// T char const* 으로타입이결정된다.

// 그러므로타입을정할때조심해서정해야한다.!!!

template<typename T> void foo( T a, T b )

{

        cout << typeid(a).name() << endl;

}


template<typename T> void goo( const T& a, const T& b )

{

        cout << typeid(a).name() << endl;

}


void main()

{

        foo( "hello", "hel" );

        goo( "hello", "hello" );

        //goo( "hello", "hel" ); // error
}

 

///////////////////////////////////////////////////////////////////
// RTTI : Run Time Type Information

class A

{

public:

        virtual ~A() {}               // 상속이라면100% 가상함수를쓰게된다.

};

 

void foo( B* p )

{

}

 

class B : public A

{

public:

};

 

void foo( A* p )

{

        // 여기서pA인지B인지를알고싶다면!!

        // B* pp = (B*)p; // error A가넘어오게되면undefine??

 

        // 방법1. typeid() 연산자사용

        const type_info& t = typeid(*p); // 6.0에서는\GR 옵션을줘야한다.

        cout << t.name() << endl; // 가상함수가있어야제대로동작한다(소멸자라도추가)

 

        if ( typeid(*p) == typeid(B) )

        {

               B* pp = (B*)p; // 안전하다.

        }

 

        // 방법2. 이거만하도록나온dynamic_cast. DYNAMIC_CAST() <-- MFC

        B* p2 = dynamic_cast<B*>(p);  // down cast 가발생하면0이리턴된다.

        if ( p2 != 0 )

        {

               // p2사용

        }

}

 

void main()

{

        B b;

 

        foo( &b );

}

 

Tag |

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

// 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 25976

가상함수 테이블(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 22182

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

#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 21249

// 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 26795

#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 27954

// 상속과 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 28228

#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 |