7.26(목) C++ 기초문법 - static 멤버

from Study/C++ 2007/07/28 23:53 view 18001

// 1. static 멤버data의특징: 모든객체가공유. 객체가없어도메모리에있다.

// 2. static 멤버함수의특징객체가없어도호출가능.

// 3. static 멤버에서는static 멤버만접근가능.

class Point

{

        int x;

        static int y;

 

public:

        void foo()

        {

               x = 10  // 1

               y = 10; // 2

        }

        static void goo()

        {

               cout << this << endl;  // error

               foo();  // error

               x = 10; // error

               y = 10; // 4

        }

};

 

int Point::y = 0;

 

void main()

{


        //클래스 내의 static 멤버 호출 시 1번으로 한다.
       
Point::goo();  // 1
일반함수인지static 함수인지를알수있다.

 

        Point p;

        p.goo();      // 2

}

 

Tag |

// 클래스가내부적으로포인터를가지면소멸자에delete 하라.!

 

// 1. 얕은복사(Shallow Copy) 현상. 해결책3가지.

//             (A). 깊은복사(Deep Copy)

//             (B). 복사를금지한다. - private에선언만제공한다.

//             (C). Referece Counting - 속도가조금빨라진다.
//               (
멀리스레드에서는오히려속도가느려진다. 동기화)



http://www.research.att.com/~bs/string_example.c
 // 창시자 코드!!

#include <iostream>

using namespace std;

 

class String

{

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

        struct SRep // 내포클래스: String에서만이클래스를사용하므로내부에넣어버리자!!

        {

               char* buff;

               int sz;

               int ref;

 

               SRep(int nsz, const char* pbuff)

               {

                       ref = 1;

                       sz = nsz;

                       buff = new char[sz+1]; // add space for terminator

                       strcpy( buff, pbuff );

               }

 

               SRep( const char* s ) : ref(1)

               {

                       sz = strlen(s);

                       buff = new char[sz+1];

                       strcpy( buff, s );

               }

               ~SRep() { delete[] buff; }

 

               SRep* get_own_copy()

               {

                       if ( ref == 1 ) return this;

                       ref--; 

                       return new SRep( sz , buff );

               }

 

               void assign(int nsz, const char* pbuff)

               {

                       if (sz != nsz) {

                              delete[] buff;

                              sz = nsz;

                              buff = new char[sz+1];

                       }

                       strcpy( buff ,pbuff );

               }

        private:

               SRep(const SRep&);

               SRep& operator=(const SRep&);

 

        };     
//////////////////////////////////////////////////////////////////////////

        class Cref

        {

               friend class String;

               String& s;

               int i;

 

               Cref(String& _s, int _i) : s(_s), i(_i) { }

               Cref(const Cref& r) : s(r.s), i(r.i) { }

               Cref();                // not defined, never used

 

        public:

               operator char() const

                {

                       s.check(i); return s.read(i);

                }

               void operator=(char c)

                {

                        s.write(i,c);

                }

 

        };
//////////////////////////////////////////////////////////////////////////

        SRep* rep;

 

public:

       

        class Range {};

 

        String()

        {

               rep = new SRep(0,"");

        }

 

        String( const char* s ) { rep = new SRep(s); }

 

        ~String() { if( --(rep->ref) == 0 ) delete rep; }

 

        // 참조개수기반의복사생성자

        String( const String& s )

        {

               rep = s.rep;

 

               ++(rep->ref);

        }

 

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

        // 참조개수기반의대입연산자

        String& operator=( const String& s )

        {

               // 모든대입연산자의기본은1번째줄에서자신과의참조를비교해야한다.

                if( &s == this ) return *this;

 

               if( --(rep->ref) == 0 ) delete rep;

 

               rep = s.rep;

 

               // 이제count 증가.

               ++(rep->ref);

 

               return *this;

        }

 

        friend ostream& operator<<( ostream& os, const String& s)

        {

               return os << s.rep->buff;

        }

 

        char operator[](int i) const

        {

               check(i);

               return rep->buff[i];

        }

        Cref String::operator[](int i)

        {

               check(i);

               return Cref(*this,i);

        }

 

        String& operator=(const char* s)

        {

               if (rep->ref == 1)

                       rep->assign(strlen(s),s);

               else {

                       rep->ref--;

                       rep = new SRep(strlen(s),s);

               }

               return *this;

        }

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

 

        void check(int i) const

        {

               if (i<0 || rep->sz<=i) throw Range();

        }

 

        char read(int i) const

        {

               return rep->buff[i];

        }

 

        void write(int i, char c)

        {

               rep=rep->get_own_copy();

               rep->buff[i] = c;

        }

};

 

void main()

{

        String s1 = "hello";

        String s2 = s1;

 

        char c = s1[1];               // s1.operator[](1)

        cout << c << endl;     // 'e'가나오면된다.

 

        s1[1] = 'A';           // s1, s2는서로다른"hello" 메모리를사용해야한다.

        cout << s1 << endl;    // "hAllo"

        cout << s2 << endl;    // "hello"

}

 

Tag |

7.25(수) C++ 기초문법 - 복사생성자

from Study/C++ 2007/07/27 00:09 view 17209

#include <iostream>

using namespace std;

 

// 복사생성자개념

// 1. 복사생성자의모양

// 2. 사용자가만들지않으면컴파일러가만들어준다.( 멤버복사를수행한다. )

// 3. 복사생성자는언제호출되는가?

//             (1) 자신의type으로초기화될때Point p2(p1);

//             (2) 함수호출시call by value - const& 로막을수있다.

//             (3) 함수가객체를값으로리턴할때.!!

 

//(3)

class Point

{

public:

        Point()                               { cout << "생성자" << endl; }

        Point( int a, int b )  { cout << "생성자" << endl; }

        ~Point()                              { cout << "소멸자" << endl; }

        Point( const Point& p1 ){ cout << "복사생성자" << endl; }

};

 

Point foo()            //Point p = p1;

{

        //Point p1(1, 2);

        //cout << "foo" << endl;

        //return p1;

 

        cout << "foo" << endl;

 

        // 리턴용도로만사용한다면만들면서리턴해라...

        // 리턴용이름없는객체만만들어진다.

        // Return Value Optimization..!!( RVO )

        return Point(1, 2);           // 생성자를사용해서리턴용객체만만든다.

}

 

void main()

{

        cout << "start" << endl;

        Point p1;

        {

               cout << "in block" << endl;

               foo();

               cout << "out block" << endl;

        }

        cout << "end" << endl;

}

 

/*

class Point

{

public:

        Point()                               { cout << "생성자" << endl; }

        Point( int a, int b )  { cout << "생성자" << endl; }

        ~Point()                              { cout << "소멸자" << endl; }

        Point( const Point& p1 ){ cout << "복사생성자" << endl; }

};

 

 

void foo( Point p )           //Point p = p1;

{

        cout << "foo" << endl;

}

 

void main()

{

        cout << "start" << endl;

        Point p1;

        {

               cout << "in block" << endl;

               foo( p1 );

               cout << "out block" << endl;

        }

        cout << "end" << endl;

}

*/

 

/*

class Point

{

public:

        int x;

        int y;

 

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

 

        // 복사생성자: 사용자가만들지않으면컴파일러가만들어준다.

        // 기본적으로멤버복사를수행한다.

        Point( const &Point p )

        {

               x = p.x;

               y = p.y;

        }

};

 

void main()

{

        Point p1;

        Point p2(1, 2);

        Point p3(p2);          // Point( Point );

 

        cout << p3.x << endl;

        cout << p3.y << endl;

 

        Point p4 = p3;         // Point p4(p3);       복사생성자호출

 

        int a = 0;

        int a(0);

}

*/

Tag |

#include <iostream>

using namespace std;

 

// 초기화리스트이야기..( Member Initialize List )

// 1. 초기화리스트모양. 특징( 대입이아닌진짜초기화이다. )

// 2. 멤버가놓여있는순서대로초기화한다.

// 3. 상수멤버와레퍼런스멤버는반드시초기화리스트로초기화해야한다.

// 4. 기본생성자가없는클래스를멤버로가질떄반드시초기화리스트를사용해서초기화해야한다.

 

class Point

{

        int x;

        int y;

public:

        Point( int a, int b ) : x(a), y(b)

        {

        }

};

 

class Rect

{

        Point p1;

        Point p2;

public:

        Rect() : p1(0, 0), p2(0, 0)

        {}

};

 

void main()

{

        Rect r;

 

        //Point p1;                   // error. 기본생성자가없다.

        Point p2( 1, 2 );      // Ok. 인자2개생성자는있다.

}

 

/*

class Point

{

public:

        int x;

        int y;

 

        const int c1;

 

//      int c = 0;     //error 아직c가메모리에있는게아니다.

                              // 객체를만들어야c는메모리에있다.

 

public:

        Point() : x(0), y(0), c1(0)           // 초기화

        {

               //x = 0;       // 대입

               //y = 0;

        }

};

 

void main()

{

//      const int c;   // error 초기값이필요.

 

 

        Point p;

        // y(10), x(y)로초기화리스트작성시undefined 대부분쓰레기값

        cout << p.x << endl;
        cout << p.y << endl;   // 10

 

        int a;  // a가객체라면생성자호출

        a = 0;  // 대입연산자가호출.

 

        int b = 0;     // 초기화. 생성자만1번호출된다.

}

*/

Tag |

// 생성자에대해서..

// 1. 생성자의모양, 언제호출되는가?

// 2. 생성자를1개도만들지않으면컴파일러가기본생성자1개를제공한다.

 

#include <iostream>

using namespace std;

 

class Point

{

private:

        int x;

        int y;

public:

        Point()                       // 1

        {

               Point(0, 0);   // 생성자에서다른생성자호출!!

               x = 0;

               y = 0;

               cout << "Point()" << endl;

        }                                    

        Point( int a, int b ) { x = a; y = b; }     // 2

};

 

class Rect

{

        Point p1;

        Point p2;

public:

        Rect()

        {

               cout << "Rect()" << endl;

        }

};

 

void main()

{

        Rect r; // ?

 

 

        Point* p5;             // 객체가아니라포인터이다. 생성자호출안됨

 

        Point* p6 = (Point*)malloc( sizeof(Point) ); //생성자호출안됨.

        Point* p7 = new Point; // 생성자호출

 

        Point p;

        //p.Point();           // 생성자의명시적호출은안된다.!!!

 

        Point p3[10];  // 1번생성자를10번호출

        Point p4[5] =
         { Point(1, 2), Point(2, 3) };  // 2
번생성자2, 1번생성자3번호출

 

        Point p1;

        Point p2( 1, 2 );

}

 

Tag |

#include <iostream>

using namespace std;

 

// stack이필요하다.

// 1. type을만들지않은경우

//    단점: Stack 2개이상필요한경우복잡해진다.

 

 

int buf[10];

int top = 0;

 

void push( int a )

{

        buf[top++] = a;

}

 

int pop()

{

        return buf[--top];

}

 

void main()

{

        push( 10 );

        push( 20 );

 

        cout << pop() << endl;

        cout << pop() << endl;

}

 

 

// 2. Stack이라는Type을먼저만들자- 구조체사용

 

struct Stack

{

        int buf[10];

        int top;

};

 

void init( Stack* s )         { s->top = 0; }

void push( Stack* s, int n)   { s->buf[s->top++] = n; }

int  pop( Stack* s )           { return s->buf[--s->top]; }

 

int main()

{

        Stack s1;

        Stack s2;

        init( &s1 );

        push( &s1, 10 );

 

        cout << pop( &s1 ) << endl;

 

        return 0;

}

 

 

// 3. 함수와Data를묶어서표현하자.

// Type 설계의원칙: 잘못사용하기어렵게만들어라.

 

struct Stack

{

private:                      // 멤버함수만접근할수있다.

        int buf[10];

        int top;

 

public:                       // 모든곳에서접근할수있다.

        // 멤버함수: 멤버Data에바로접근할수있다.

        //void init() { top = 0; }            //생성자역할.

        Stack() { top = 0; }

 

        void push( int n ) { buf[top++] = n; }

        int  pop() { return buf[--top]; }

};

 

void main()

{

        Stack s1;

        Stack s2;

 

        //s1.init();

        s1.push(10);

 

        //s1.top = 100;               // private: 영역!!

 

        cout << s1.pop() << endl;

}

 

// 4. 동적메모리할당사용

 

struct Stack

{

private:                     

        int* buf;

        int top;

 

public:                      

        // 생성자: 객체를생성하면자동으로호출되는함수

        Stack( int sz = 10 )

        {

               top = 0;

               buf = new int[sz];

        }

 

        // 객체가파괴될때자동으로호출된다.

        ~Stack() { delete[] buf; }

 

        void push( int n ) { buf[top++] = n; }

        int  pop() { return buf[--top]; }

};

 

void main()

{

        Stack s1;

        Stack s2(30);

 

        s1.push(10);

 

        cout << s1.pop() << endl;

}

 

 

// 5. Template 기반의Stack

 

template <typename T>

struct Stack

{

private:                     

        T* buf;

        int top;

 

public:                      

        // 생성자: 객체를생성하면자동으로호출되는함수

        Stack( int sz = 10 )

        {

               top = 0;

               buf = new T[sz];

        }

 

        // 객체가파괴될때자동으로호출된다.

        ~Stack() { delete[] buf; }

 

        void push( T n ) { buf[top++] = n; }

        T    pop() { return buf[--top]; }

};

 

void main()

{

        Stack<int> s1;       // template class( struct )의객체를생성하는방법

        Stack<double> s2(30);

 

        s1.push(10);

 

        cout << s1.pop() << endl;

}

 

template <typename T>

struct List

{

        T buf;

        List* link;

 

        List()

        {

               link = NULL;

        }

};

 

template <typename T>

struct Stack

{

private:                     

        List<T>* pList;

        int tail;

 

public:                      

        // 생성자: 객체를생성하면자동으로호출되는함수

        Stack()

        {

               tail = 0;

        }

 

        // 객체가파괴될때자동으로호출된다.

        ~Stack()

        {

        }

 

        void push( T n )

        {

               pList = new pList;

               pList->buf = n;

        }

 

        T pop()

        {

               return buf[--top];

        }

};

 

void main()

{

        Stack<int> s1;   // template class( struct )의객체를생성하는방법

 

        s1.push(10);

 

        cout << s1.pop() << endl;

}

 
6. Single linked list 기반의 template Stack

#include <iostream>

using namespace std;

 

template <typename T>

class List

{

public:

        T m_Data;

        List* m_pNext;

 

        List(T n, List* next) : m_Data(n), m_pNext(next)

        {}

        ~List()

        {

               if( m_pNext != NULL )

                       delete m_pNext;

        }

};

 

template <typename T>

class Stack

{

        List<T>* m_pList;

 

public:

        Stack();

        ~Stack();

 

        bool IsEnd();

 

        void push( T n );

        T pop();

};

 

template<typename T>

Stack<T>::Stack() : m_pList(NULL)

{}

 

template<typename T>

Stack<T>::~Stack()

{}

 

template<typename T>

bool Stack<T>::IsEnd()

{

        return (m_pList == NULL);

}

 

 

template<typename T>

void Stack<T>::push( T n )

{

        m_pList = new List<T>( n , m_pList );

}

 

template<typename T>

T Stack<T>::pop()

{

        if( IsEnd() )

        {

               cout << "Stack is End" << endl;

               return -1;

        }

       

        List<T>* temp = m_pList;

        T data  = m_pList->m_Data;

        m_pList = m_pList->m_pNext;

 

        temp->m_pNext = NULL;

        delete temp;

 

        return data;

}

 

void main()

{

        Stack<int> s1;

        s1.push( 10 );

        s1.push( 20 );

        s1.push( 30 );

 

        cout << s1.pop() << endl;

        cout << s1.pop() << endl;

        cout << s1.pop() << endl;

 

        Stack<double> s2;

        s2.push( 4.20 );

        s2.push( 5.560 );

 

        cout << s2.pop() << endl;

        cout << s2.pop() << endl;

 

//      Stack<char*> s3;

//      s3.push("문자열??");

//      s3.push("출력가능??");

//      cout << s3.pop() << endl;

//      cout << s3.pop() << endl;

//      cout << s3.pop() << endl;

}

 

Tag |

7.25(수) C++ 기초문법 - OOP 개념

from Study/C++ 2007/07/26 23:59 view 18524

// OOP 개념= 필요한Type을먼저설계하자. ( 프로그램이훨씬간단해진다. )

// C: 객체기반(Data만으로Type을설계한다.)

// C++: 객체지향( Data+Function 으로Type을설계하자. )

 

#include <iostream>

using namespace std;

 

struct Complex

{

        int real;

        int image;

 

        Complex Add( Complex c1, Complex c2 )

        {

        Complex temp;

 

        temp.real  = c1.real  + c2.real;

        temp.image = c1.image + c2.image;

 

        return temp;

        }

};

 

void main()

{

        Complex c1 = { 1, 1 };

        Complex c2 = { 2, 2 };

 

        Complex c3 = c3.Add( c1, c2 );

}

 

/*

//복소수2개의합을구하고싶다.

 

 void Add( int ar, int ai, int br, int bi,    // in Parameter

         int* sr, int* si )                   // out Parameter

{

        *sr = ar + br;

        *si = ai + bi;

}

 

void main()

{

        int ar = 1, ai = 1;           // 1+ 1 i

        int br = 2, bi = 2;           // 2+ 2 i

        int sr, si;

 

        Add( ar, ai, br, bi, &sr, &si );

}

*/

Tag |

6교시

//
동적메모리할당
 

// 1. 왜동적메모리할당을사용하는가?

//    주로실행시간결정된값으로메모리를할당하기위해.


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

// 배열의크기에대해서


void
foo( const int c )

{

        int ar[c];        // error. 실행해봐야알수있다.(컴파일시간에결정!!)

}

 

int main()

{

        const int x = 10;

        int ar[x];       // ok.

 

        const int sz[3] = { 1, 2, 3 };

        int br[ sz[1] ]; 
       
// error.
배열의크기는상수가아니라

        // 컴파일시간에크기를알아야하는것이다.

        // sz[1] => *(sz+1)

}

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

void main()

{

        //C++

        int* p1 = new int;

        int* p2 = new int[10];

 

        delete p1;

        delete[] p2;

 

        int* p3 = new int(3);  // 3으로초기화

 

        // 2차원배열의주소는1차원배열포인터로가르키면된다.

        int(*p4)[10] = new int[10][10];              // 2차원배열형태로.

       

        int* p6 = new int;

        free(p6);              // undefined

 

        int* p7 = new int[10];

        delete p7;             // undefined

 

        int n;

        cout << "학생수를넣어주세요. >>";

        cin >> n;

 

        // 학생수만큼의메모리를할당하고싶다.   

        //int jumsu[n]; 
       
//
배열의크기는변수가될수없다. 상수이어야한다.

 

        int* jumsu = (int*)malloc( sizeof(int)*n );         

}

Tag |

5교시

//
리턴값과레퍼런스이야기

 

// 값을리턴하는함수

// 1) bulit in type 의값을리턴( ex. int foo() )

//      => 리턴값은상수취급된다.

// 2) user type의값을리턴( ex. Point foo() )

//      => 임시객체를리턴한다.( 일부컴파일러는임시객체를상수취급하기도한다.)

int x = 10;

 

int& foo()

{

        return x;

}

 

void main()

{

//      int s = foo();        // 값을리턴하면10 = 30의의미를가진다.

        foo() = 30;           // &을리턴하면된다.

                               

 

         cout << x << endl;

}

 

//================================================

// 1. 함수가레퍼런스를리턴하는이유.

//    (1) built in type의레퍼런스( int& foo() )

//             => "함수호출() = "의표현을가능하게하기위해

//             => 함수호출이lvalue에놓이기위해.

 

//    (2) user type의레퍼런스-> 임시객체의생성을막기위해사용.

//             ex) Point& foo()

 

struct Point

{

        int x;

        int y;

};

Point p = { 1, 2 };

 

Point& foo()

{

        return p;

}

 

void main()

{

        foo().x = 10;          // user type의리턴값은상수가아니다.

 

        cout << p.x << endl;

}

Tag |

7.24(화) C++기초문법 - reference

from Study/C++ 2007/07/24 17:14 view 20206

4교시

//
레퍼런스문법총정리

// 1. 개념: 기존메모리에대한별명.

int main()

{

        int  n = 10;

        int* p = &n;

        int& r = n;            //

 

        r = 20;

 

        cout << n << endl;     // 20

 

        int& r2;               // ? Error. 반드시초기화되어야한다.

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

        int*& rp = p;  // ok. 포인터의별명

        int&* pr = r;  // 별명의주소?  error

}


 

// 2. 원리: 내부적으로대부분의컴파일러는포인터를사용한다.

//         "자동으로역참조되는포인터" 라고도부른다.

void inc1( int x )     { ++x; }

void inc2( int* p )    { ++(*p); }

void inc3( int& r ) { ++r; }

 

void main()

{

        int a = 10;

        int b = 10;

        int c = 10;

 

        inc1( a );             // 실패- call by value

        inc2( &b );            // ok. call by pointer

        inc3( c );             // ok. call by reference int &r = c;

 

        cout << a << endl;     // 10

        cout << b << endl;     // 11

        cout << c << endl;     // 11

}


 

// 3. 함수호출과레퍼런스

 

// 인자값을변경하는함수-> 포인터가좀더좋다. ( 명확하다. )

// 인자값이바뀌지않은함수

// 1) bulit in type => const T&가오히려나쁘다.(4byte사용, 레지스터사용못함.)
//                     call by value
를사용.

// 2) user type     => const T&


struct AAA

{

        int data[10000000];   // 1M라고가정.

};

 

void foo( const AAA& a )   // call by value :  값을 변경하지 않을것 이다.

{                          // 단점: 메모리 사용량이 2개가 된다.

        a = 20;            // const& : 메모리 사용량을 줄이고 값을 변경하지 않게 한다.

}

 

void main()

{

        AAA x = 10;

 

        foo ( x );

       

        cout << x << endl;

}


 

Tag |