템플릿은 STL 제너릭 프로그래밍의 핵심이다.

 

템플릿을 사용하는이유?

: 범용적으로 사용함에 있어, 미리 타입을 정하지 않고,  일반화(generic)된 프로그래밍을 하기위함

 

컴파일러가 함수 호출인자 타입을 보고 템플릿 함수의 매개변수 타입을 결정하여 실제 함수인 템플릿 인스턴스 함수를 만들어 냅니다.

 

 

 

[함수 템플릿]

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
 
#include <iostream>
using namespace std;
 
/**
*    템플릿
*/
 
class Student{
    int kor_score;
    int eng_score;
public:
    Student(int _kor, int _eng ) : kor_score(_kor), eng_score(_eng){}
    void Print(int num){
        cout<<"["<<num<<"]"<<"한국사 점수 : "<<kor_score<<"점"<<endl;
        cout<<"["<<num<<"]"<<"영어 점수 : "<<eng_score<<"점"<<endl;
    }
};
 
 
template <typename T1, typename T2>    //template<class T1, class T2>
void Print(T1 arg1, T2 arg2){
    cout<< arg1 << "," << arg2 <<endl;
}
 
//템플릿특수화
template<>
void Print(int num, Student * st){
//void Print<>(int num, Student * st){
//void Print<int, Student*>(int num, Student* st){
    st->Print(num);
}
 
template <typename T1, int size>
void PrintArray( T1* arr ){
    for(int i = 0; i < size; ++i ){
        cout<<"["<<i<<"]"<<arr[i]<<endl;
    }
}
 
int main()
{
    Print(10, 20);
    Print(0.4, 0.05);
    Print("ABCD", 10);
    cout<<endl;
 
    Print<int,int>(10, 20);                //함수템플릿타입을 명시적호출
    Print<doubledouble>(0.4, 0.05);
    Print<const char*,int>("ABCD", 10);
    cout<<endl;
    //----------------------------------------------------------
 
    Student s1(100, 50);
    Student s2(90, 30);
    Print(0,&s1);    // 특수화 버전 호출
    Print(1,&s2);    // 특수화 버전 호출 Print<Point>(&s2);->명시적호출
    cout<<endl;
    //----------------------------------------------------------
    int arr[5] = { 1, 2, 3, 4, 5 };    
    PrintArray<int, 5>(arr);
    // 여기서 이렇게 템플릿을 명시적으로 호출해야만 한다.
    // 왜냐하면 T1이라는 정보만을 제공하므로 5라는 템플릿 매개변수 인자를
    // 클라이언트 코드만으로 컴파일러가 추론할 수 없기 때문
 
    //-----------------------------------------------------------
 
    system("pause");
    return 0;
}

 

 

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
#include <iostream>
#include <string>
using namespace std;
 
/**
* 함수객채(printFunctor)를 사용한 For_each
*/
 
template <typename IterT, typename Func>
void For_each(IterT begin,IterT end, Func pf )
{
    while( begin != end )
    {
        pf( *begin++ ); 
    }
}
 
template <typename T>
struct PrintFunctor
{
    string sep; // 출력 구분자 정보 seperator
public:
    explicit PrintFunctor(const string& s=" "):sep(s) { }
    void operator()(T data) const
    {
        cout << data <<sep;
    }
};
 
void main( )
{
    int arr[5] = {10, 20, 30, 40, 50};
    For_each(arr, arr+5, PrintFunctor<int>());  // 명시적 호출을 해줘야 한다.
    cout << endl;                                // 그렇지 않으면 컴파일러가 
                                                // typename T 를알수없다.
 
    string sarr[3] = {"abc","ABCDE","Hello!"};
    For_each(sarr, sarr+3, PrintFunctor<string>("*\n")); // 구분자 *
    cout << endl;
}
 
 

 

 

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
#include <iostream>
#include <string>
using namespace std;
 
/**
*    반환타입과 매개변수 타입을 인자로 갖는 함수객체
*/
 
template <typename RetType, typename ArgType>
class Functor
{
public :
    RetType operator( ) (ArgType data)
    {
        cout << data << endl;
        return RetType();
    }
};
void main( )
{
    /**
    *    템플릿의 매개변수와 함수 객체를 결합하면 반환타입과
    *    함수매개변수 타입을 클라이언트가 결정하는 아주 유연한
    *    함수객체를 만들수 있습니다.
    */
    Functor< voidint > functor1;
    functor1( 10 );
    Functor< bool, string > functor2;
    functor2( "Hello!" );
}

 

 

[클래스 템플릿]

 

 

실제로 메타 클래스(클래스를 만드는 클래스)이다. 그리고 이것을 메타프로그래밍이라고 한다. 본질적으로 메타프로그래밍은 프로그램 작성시가 아닌 실행 중 코드를 변경하거나 생성함을 말한다.

 

 

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
#include <iostream>
#include <string>
using namespace std;
 
/**
*    클래스 템플릿
*    :클래스를 만들어내는 틀(Meta코드)
*/
 
#include <iostream>
using namespace std;
 
template < typename T1=intint capT = 100 > //디폴트 매개변수
class Array{
    T1 *arr;
    int size;
    int capacity;
 
public:
    explicit Array(int cap = capT) : arr(NULL), size(0), capacity(cap) {
        arr = new T1[capacity];
    }
    ~Array(){
        delete [] arr;
    }
 
    void Add( T1 data ){
        if( size > capacity)
            throw "오바";
 
        arr[size++] = data;    
    }
    void print() const{
        for(int i = 0; i < size; ++i)
            cout<<"["<<i<<"] "<<arr[i]<<endl;
    }
    int Size() const{
        return size;
    }
    T1& operator[](int idx){
        return arr[idx];
    }
    T1 operator[](int idx) const{
        return arr[idx];
    }    
};
 
int main(){
    
    Array<int> arr; // 정수(클라이언트가 T타입 결정) Array객체
    //Array<> arr;    // 디폴트 매개변수값을 갖는 선언
    try{
        arr.Add(10);
        arr.Add(20);
        arr.Add(30);
    }catchchar * pStrException ){ cout<<pStrException<<endl; }    
    arr.print();
 
    Array<string> sarr; // 문자열
    try{
        sarr.Add("abc");
        sarr.Add("ABC");
        sarr.Add("Hello!");
    }catchchar * pStrException ){ cout<<pStrException<<endl; }    
    sarr.print();
 
 
    system("pause");
    return 0;
}

 

 

 

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
#include <iostream>
#include <string>
using namespace std;
 
/**
*    클래스 템플릿 특수화
*/
template <typename T>
class ObjectInfo
{
    T data;
public:
    ObjectInfo(const T& d):data(d) { }
    void Print( )
    {
        cout <<"타입 : "<< typeid(data).name() << endl;
        cout <<"크기 : "<< sizeof(data) << endl;
        cout <<"값 : "<< data << endl;
        cout <<endl;
    }
};
template <> // T를 string으로 특수화(클래스 템플릿 특수화)
class ObjectInfo<string>
{
    string data;
public:
    ObjectInfo(const string& d):data(d) { }
    void Print( )
    {
        //cout <<"타입 : "<< typeid(data).name() << endl;
        cout <<"타입 : "<< "string" << endl; //타입 정보 수정
        //cout <<"크기 : "<< sizeof(data) << endl;
        cout <<"문자열 길이 : "<< data.size() << endl; //길이 정보 수정
        cout <<"값 : "<< data << endl; 
        cout <<endl;
    }
};
void main( ) 
{
    ObjectInfo<int> d1(10);
    d1.Print( ); // 객체 정보 출력
 
    ObjectInfo<double> d2(5.5);
    d2.Print( ); // 객체 정보 출력
 
    ObjectInfo<string> d3("Hello!");
    d3.Print( ); // 객체 정보 출력
}
 

 

 

 

 

'0x0001 > C, C++' 카테고리의 다른 글

[C++] 콜백함수( CallBack Function )  (0) 2019.02.08
[C++] 정적함수포인터, 멤버함수포인터  (0) 2019.02.08
[C++] 연산자 오버로딩  (0) 2019.02.08
[C++] 가상소멸자  (0) 2019.02.08
[C언어] 10진수를 2진수로  (0) 2019.02.08

 

 

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
#include <iostream>
 
/**
* 연산자 오버로딩
*/
 
using namespace std;
 
class Point {
 
    int x;
    int y;
 
public:
    explicit Point( int _x, int _y ){ x = _x; y = _y; }    
 
    /**
    * 단항
    */
    const Point&operator++(){
        ++x;
        ++y;
        return *this;
    }
    const Point operator++(int){
        Point tmp( x, y );
        ++x;
        ++y;
        return tmp;
    }
 
    /**
    * 이항
    */
    bool operator== ( const Point& p2 ){
 
        return x == p2.x && y == p2.y ? true : false;
    }
    void Print() { cout<<x<<","<<y<<endl;}
 
 
    /**
    * 배열인덱스 연산자
    */
 
    int operator[] (int idx ) const {
        if( idx == 0 ) return x;
        else if( idx == 1 ) return y;
        else throw "이럴순 없다";
    }
};
 
int main()
{
    Point p1(10, 20), p2(10, 20);
    Point ret(0,0);
 
//    ret = p1++;
 
    p1.Print();
    ret.Print();
 
    bool bRet = p1 == p2;
    printf("%d\n", bRet);
 
 
    try
    {
        Point(1,2)[3];
    }
    catch(char* pStrexception){
        printf("%s\n", pStrexception);
    }
     
    system("pause");
    return 0;
}

 

 

//스마트 포인터 ->,* 연삱자 오버로딩

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
#include <iostream>
using namespace std;
 
class Point
{
    int x;
    int y;
public:
    Point(int _x =0 , int _y =0 ):x(_x),y(_y) { }
    void Print( ) const { cout << x <<',' << y << endl; }
};
class PointPtr
{
    Point *ptr;
public:
    PointPtr(Point *p):ptr(p) { }
    ~PointPtr( )
    {
        delete ptr;
    }
    Point* operator->()const
    {
        return ptr;
    }
    Point& operator*()const
    {
        return *ptr;
    }
};
void main()
{
    Point* p1 = new Point(2,3); //일반 포인터
    PointPtr p2 = new Point(5,5); //스마트 포인터
 
    p1->Print(); //p1->Print() 호출 
    p2->Print(); //p2.operator->()->Print() 호출 
    cout << endl;
 
    (*p1).Print(); //(*p1).Print() 호출
    (*p2).Print(); //p1.operator*().Print() 호출
 
    delete p1;
    //p2의 소멸자에서 Point 동적 객체를 자동 메모리 제거합니다.
}

 

 

 

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
 
/**
* 함수 호출 연산자오버로딩()
*/
 
#include <iostream>
 
 
using namespace std;
 
 
 
class FuncObj{    
public:
    void operator() (int arg) const {
        cout<<"정수 : "<<arg<<endl;        
    }
 
    void operator() (int arg1, int arg2 ) const {
        cout<<"정수 : "<<arg1<<","<<arg2<<endl;
    }
 
    void operator() (int arg1, int arg2, int arg3 ) const {
        cout<<"정수 : "<<arg1<<","<<arg2<<","<<arg3<<endl;
    }
};
 
 
void Print1(int arg){
    cout<<"정수 : "<<arg<<endl;
}
 
 
int main(){
 
    void (*Print2)(int) = Print1;
    FuncObj Print3;
 
    Print1(10);    // 함수호출을 이용한 연산자 오버로딩
    Print2(10); // 함수포인터를 이용한 연산자 오버로딩
    Print3(10); // 함수객체를 이용한 연산자 오버로딩
    FuncObj()(10,20,30); // 임시객체를 이용한 연산자오버로딩된 함수호출
                         // 임시객체는 이문장이 끝이나면 소멸된다.
 
 
    system("pause");
    return 0;
}

 

 

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
/**
* 배열 인덱스 연산자오버로딩
*/
 
#include <iostream>
using namespace std;
 
class Array{
 
    int *arr;
    int size;
    int capacity;
 
public:
    explicit Array(int cap = 1) : arr(NULL), size(0), capacity(cap) {
        arr = new int[capacity];
    }
 
    void Add( int data ){
 
        if( size > capacity)
            throw "오바";
        arr[size++] = data;    
    }
    int Size() const{
        return size;
    }
    int& operator[](int idx){
        return arr[idx];
    }
    int operator[](int idx) const{
        return arr[idx];
    }    
};
 
 
int main(){
 
    Array ar(10);
 
    try{
        ar.Add(10);
    }catchchar * pStrException ){
 
        cout<<pStrException<<endl;
    }    
    try{
        ar.Add(20);
    }catchchar * pStrException ){
 
        cout<<pStrException<<endl;
    }    
    try{
        ar.Add(30);
    }catchchar * pStrException ){
 
        cout<<pStrException<<endl;
    }    
 
    ar[0] = 90;
    const Array &ar2 = ar;
    //ar2[0] = 100;
    
    forint i = 0; i < ar.Size(); ++i )
        cout<< ar[i] <<endl;
    cout<<ar2[0]<<endl;
 
    system("pause");
    return 0;
}

 

Point operator+(const Point& p );

 

멤버함수로 오버로딩된경우

: p1.operator+( p2 ) ;

 

전역함수로 오버로딩 된경우

: operator+(p1, p2);

 

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
#include <iostream>
 
using namespace std;
 
class A{
 
public:
    A(){cout<<"A생성자 호출"<<endl;}
    /*virtual*/ ~A(){
        cout<<"A소멸자 호출"<<endl;
    }
};
 
 
 
class B : public A
{
public:
    int bb; 
 
    B(){cout<<"B생성자 호출"<<endl;}
    ~B(){cout<<"B소멸자 호출"<<endl;}
 
};
 
 
 
class C : public B
{
 
public:
    int cc; 
 
    C(){cout<<"C생성자 호출"<<endl;};
    ~C(){cout<<"C소멸자 호출"<<endl;};
 
};
 
int main()
{
//  C * c = new C();
//  delete c;
 
    /** 생성자호출
    * C클래스로 할당하였으므로, B로 할당하면 B부터
    * C클래스 생성자로 들어가보니 B클래스를 상속받아서 B클래스를 들어가보니
    * A클래스를 상속받았으므로 A클래스의 생성자를 호출후 지금까지 호출된
    + 생성자들각각 호출해주니 a->b->c
    * a(생) -> b(생) -> c(생)
    */
 
    /** 소멸자호출
    * C클래스 였기때문에 상속받은 순서반대로 
    * 즉! C클래스를 삭제하였으니까 C소멸자를 먼저호출하고나서,
    * 들어가보니 B클래스를 상속받았으니까 B클래스 소멸자를 호출
    * 그다음에 A소멸자를 호출
    * c(소)-> b(소) -> a(소)
    */
 
//-----------------------------------------------------------------------------
 
//    A * a = new C();
//    delete a; 
 
    /** 생성자호출
    * 이것도 위와 마찬가지로 C클래스로 항당하였으므로 
    * 상속받은 부모클래스를 차례대로 들어가게되고 끝까지 들어가서 생성자를 
    * 호출하면서 C클래스 생성자까지 내려오게된다.
    * a(생성자)-> b(생성자)-> c(생성자)
    */
 
    /** 소멸자호출
    * 이부분이 중요
    * A클래스를 삭제하였기때문에 먼저 A클래스의 소멸자를 호출하는데
    * A클래스는 상속받은게 없고 virtual 키워도로 소멸자가 감싸지도
    * 않았기때문에 소멸자는 a(소멸자) 한번만 호출되어 메모리누수
 
    * 해결방법은 A클래스의 소멸자에 virtual 키워드를 붙이는것으로 해결
    * A클래스 소멸자를 봤더니 virtual이다 하면, A클래스를 상속받은 자식
    * 소멸자에게 내려가게되고, 그자식도 virtual의 속성을 물려받게 되며,
    * 그자식을 상속받은 자식까지 계속 있을때까지 내려가게되서
    * 마지막 소멸자부터 호출하게 된다. 그리고 호출스택에 쌓인 생성자
    * 들을 차례차례 호출함으로써 끝이난다.
    * c(소멸자)->b(소멸자)->a(소멸자)
    */
 
//--------------------------------------------------------------------------
 
    B * b = new C();
    delete b;
 
    /** 생성자호출
    * 이것또한 위와 같다. C클래스로 할당하였으니까
    * a(생성자)->b(생성자)->c(생성자)
    */
 
    /** 소멸자호출
    * B클래스 소멸자를 봤더니 A클래스를 상속받았으므로 B소멸자를
    * 호출하고 나서, A클래스의 소멸자를 호출하게 된다.
 
    * 이렇게 되면, C클래스의 소멸자가 호출되지 않아 메모리 누수 
    * 해결방법은 B클래스의 생성자나 A클래스 소멸자에게 virtual 
    * 키워드를 붙이게되면, 자동으로 클래스를 상속받은 자식들은
    * virtual을 상속받게되어 모든 소멸자가 호출된다.
    * B클래스의 소멸자에 virtual을 붙이는경우는 
    * B클래스의 소멸자를 들어가보니 virtual 이라서 B클래스를 상속받은
    * 자식을 찾아 내려가게되고, 여기서는 C클래스 소멸자이므로, C까지 
    * 내려와서 C부터 소멸자를 호출하고, 호출스택에 쌓인 소멸자를 하나하나
    * 빼호출하므로, c(소멸자)->b(소멸자)->a(소멸자)
    */
 
    system("pause");
    return 0;
}

 

 

클래스 B는 A를 상속받았기때문에

 

부모는 자식으로 생성가능하고,

 

이때 부모를 소멸시, 부모에 가상소멸자가 아니면, 자식의 소멸자는 호출되지 않는다. 메모리 누수

 

또한

 

B * b = new B(); 는 자식을 delete하면 부모의 가상소멸자 상관없이 상속받은 base클래스의 소멸자까지 호출합니다.

 

virtual 키워드는 부모클래스에 적용됩니다.

 

 

-주의-

 

무조건 소멸자에 virtual을 붙이는건 안좋음
 - virtual함수가 있으면 vptr(가상 함수 테이블 포인터)가 발생하기 때문에 용량이 커지고 다른 언어와의 호완성이 떨어지게 된다.

여러 클래스로 파상되는 기본 클래스 일때는 소멸자에 virtual을 붙여야 함
 - 그렇지 않으면 파생된 클래스를 삭제 할때 기본 클래스의 소멸자만 호출이 되서 파생된 부분은 삭제도 못하고 그대로 남는다.
 - 가상 소멸자가 안붙으면 기본 클래스로 쓸 생각이 없는것으로 판단하는게 좋음

가상 소멸자가 없는 클래스는 상속받으면 무지 위험해짐
 - class SpecialNode : public Node //이 때 Node는 가상 소멸자가 없다고 하자
 - SpecialNode *pSN = new SpecialNode("일단은 아무 내용");
 - Node  * pNode;
 - pNode = pSN;
 - delete pNode; //이상황에서 메모리가 제거 되는건 Node의 메모리만 제거됨
 - STL컨테이너 들은 전부 가상 소멸자가 없으니 조심

 

 

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
#include <stdio.h>
 
 
int main()
{
 
    unsigned int iNum; //십진수
    int iCnt; //자릿수
 
    printf("십진수를 입력 : ");
    scanf("%d", &iNum);
    printf("%d의 이진수는 ", iNum);
 
    for( iCnt = sizeof(iNum) * 8 - 1; 0 <= iCnt; --iCnt ){
 
        //( iNum >> iCnt ) & 1    :    쉬프트 연산자를 이용해 한 비트씩 1과 &연산한다
        printf("%d", (iNum>>iCnt)&1);
        if( 0 == (iCnt%4) ){
            putchar(' ');
        }
    }
    putchar('\n');
    return 0;
}
 


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
#include <stdio.h>
 
void f( int num1, int num2 ){
 
    int num3 = 1;
    while(num3 != 0 ){
 
        num3 = num1 % num2;
        num1 = num2;
        num2 = num3;
    }
    printf("유클리드 호제법 최대공약수는 %d입니다.\n", num1 );
 
}
int main()
{
    int n1, n2;
    fputs("최대공약수 인자 설정 : ", stdout);
    scanf("%d %d", &n1, &n2 );
 
    int i, max;
    for(i = 1; i <= n1; i++)
    {
        if( n1 % i == 0 && n2 % i == 0 ){
            max = i;
        }
    }
    printf("%d와 %d의 최대 공약수는 %d입니다.\n", n1, n2, max);
 
    f(n1, n2);
 
 
 
    ////////////////////////////////////다른방법
    int ucNum;
    ucNum = n1;
    if( n1 > n2)
        ucNum = n2;
 
    for(; 0<ucNum; --ucNum){
        if( n1 % ucNum == 0 && n2 % ucNum == 0 ){
 
            printf("최대공약수: %d, 최소공배수 : %d \n", ucNum, n1*n2/ucNum );
            break;
        }
    }
 
    return 0;
}

 

 

 

약수란? 

1은 모든 수의 약수이고, 어떤 수는 자기 자신의 약수이다. 또한 어떤 정수도 0으로 나눌 수 없다. 따라서 0은 어떤 수의 약수도 아니다.

 


 

 

1) 공약수 : 두 개 이상의 자연수의 약수 중에서 공통인 것




--------------------------------------------------------------------------------------------------------

1. 최대공약수

(1) 공약수 : 두 개 이상의 자연수의 약수 중에서 공통인 것



최대공약수

(2) 최대공약수 : 공약수 중에서 가장 큰 수
(3) 최대공약수의 성질 : 두 개 이상의 자연수의 공약수는 그들의 최대공약수의 약수이다.
(4) 서로소 : 공약수가 1뿐인 두 자연수

2. 최대공약수 구하는 법

(1) 나눗셈을 이용한 방법
① 공통인 소인수로 각 수를 나눈다.
② 몫이 서로소가 될 때까지 계속 나눈다.
③ 공통으로 나온 소인수를 모두 곱한다.


------------------------------------------------------------------------------------------------------------------

  

1. 최소공배수

(1) 공배수 : 두 개 이상의 자연수의 공통인 배수



최소공배수

(2) 최소공배수 : 공배수 중 가장 작은 수

(3) 최소공배수의 성질 : 두 개 이상의 자연수의 공배수는 그들의 최소공배수의 배수이다.

2. 최소공배수 구하는 방법

(1) 나눗셈을 이용한 방법
① 두 수 이상의 공통인 소인수로 각 수를 나눈다.
② 나누어 떨어지지 않는 수는 그대로 내린다.
③ 몫이 서로소가 될 때까지 계속 나눈다.
④ 공통으로 나온 소인수에 마지막 몫을 모두 곱한다.


 

최소공배수는 두수의 곱에 최대공약수로 나눠주면 됩니다.

 

 

 

이걸 왜하냐?

 

최소공배수 -> 3일마다 오는 기차와 7일마다 오는 배가 있다. 오늘 기차와 배가 왔다면 몇일 후에 기차와 배가 동시에 올까? ( 21일 )

 

1 |3, 7

    ----

    3  7

최대공약수 1, 최소공배수 3*7 /1 = 21일

 

--------------------------------------------------------------------------------------------------------------------------

최대공약수 -> 과자 20봉지와 음료수 12캔이 있다. 이걸 공평하게 나눠주려 하면 최대 몇명에게 나눠줄 수 있을까? (하나의 과자나 음료수를 나눠서는 안된다.)


정리하자

 

i am a boy -> yob a ma i 바꾸는 코드

 

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
#include <stdio.h>
#include <tchar.h>
#include <string>
 
void getReverseStr( TCHAR * str ){
    
    // NULL문자를 제외한 문자열 길이를 리턴
    int len= _tcslen(str);    
    for(int i =0; i < len/2; ++i ){
 
        // 배열이 0 부터시작이므로, 문자열길이 - 1 
        TCHAR temp = str[i];
        str[i] = str[len-i-1];
        str[len-i-1] = temp;
    }
 
    _tprintf(L"Reverse : %s\n" , str);
}
 
int main(){
 
    /**
    * wprintf는 지역 설정에 영향을 받는 함수입니다.
    * 그래서 한글 출력을 하기 전에 지역을 설정하는 함수를 호출해야 합니다.
    * 이를 위해 Visual C++은 setlocale, _wsetlocale이라는 함수들을 제공합니다.
    * 대한민국으로 설정하려면 다음과 같이 호출해 주세요.
    */
 
    _wsetlocale(LC_ALL, L"korean");
 
    TCHAR pStr[100] = _T("i am a boy");
    getReverseStr(pStr);
    system("pause");
    return 0;
}

 

 

 

 

i am a boy -> yob a ma i -> boy a am i 로 변환하는코드

 

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
char * reverse_string( const char* ori )
{
    /**
     * 문자열을 fgets함수로 입력받으면 enterkey 문자까지 함께 들어간다.
     */
    int arrayCnt = 0, oriSize = strlen(ori)+1;
    char * rStr = (char*)(malloc(sizeof(char) * oriSize) );
    memset(rStr, 0, sizeof(char) * oriSize );
 
    //oriSize -= 2; // NULL문자 ENTER 키값 빼주고
    do{
        if( ori[oriSize] != 0 && ori[oriSize] != 10 )
         rStr[arrayCnt++]= ori[oriSize];
    }while( --oriSize >= 0 );
 
    rStr[arrayCnt] = '\0';
 
    /**
     * NULL 문자를 위에서 다시 넣어주는 이유는
     * 바꿔준 문자의 끝을 알리기 위해서 넣어주는 것이다.
     * 그렇지 않으면, 쓰게기 값이 들어간다. 
     * 처음부터 rStr을 초기화 해주는것도 방법이 될수있다.
     */
    return rStr;
}
char * reverse_word( const char* ori )
{
    /**
     * 입력받은 스티링이 yob_a_ma_i\0 총 10바이트 == gets함수
     * gets함수로 스트링을 받을경우 동적배열 생산시 길이에 + 1 해줘야함
     * 안해주면 0 - 9 즉 10개의 배열로 구성됨 사실상 \0 문자를 넣을때가 없어짐     
     * 하지만 fgets함수로 받을경우 enterkey 포함 11바이트 길이가 넘어온다.
     * +1 안해줘도 문자열 갯수에 맞게 널문자까지 알아서 들어감
     * 하지만 함수에서 문자열끝을 입력하므로 +1을 나둬도 상관없고, 
     * 1바이트가 아까으면 fgets함수와 gets함수에따라 바꿔주자
     */
    
    int arrayCnt = 0, wordCnt = 0, oriSize = strlen( ori )+1;
    char * rStr = (char*)(malloc(sizeof(char) * oriSize) );        
    memset(rStr, 0, sizeof(char) * oriSize ); 
 
    size_t cnt = 0;
    const char *p, *t; 
 
    for( p=ori; *ori; )
    { 
        // 공백이나 문자열의 끝이 나오는 위치 찾기 (fgets함수 enterkey 체크포함)
        while( *ori != ' ' && *ori != 0 && *ori != 10 ) 
        {
            ori++; 
        }
 
        // 현재 위치를 임시로 저장.     
        t = ori+1;
 
        // 현재 위치부터 거꾸로 출력 문자열에 저장. 
        while( ori != p ) 
        {
            rStr[cnt++] = *(--ori); 
        }
 
        // 하나의 단어가 끝나면 공백 삽입 
        rStr[cnt++] = ' ';
        p=t, ori=t;
 
        if( cnt == oriSize )
            break;
    } 
    // 문자열의 끝 마크. 
    rStr[cnt-1] = '\0'
 
    return rStr;
}
int main()
{
    /**
     * i am a boy -> yob a am i -> boy a am i 바꾸기
     */
    //char * str1 = "i am a boy";
    char str1[20];
    memset( str1, 0, sizeof(str1));
 
    fputs("InputString : ", stdout );
    //fgets(str1, sizeof(str1), stdin);
    gets(str1);
 
//    char * str2 =reverse_string( str1 );
//    puts(str2);
    // 이함수에 전달할때 쓰레기값.... 일단 생각해보자
 
    char * str3 =reverse_word( str1 );    
    puts(str3);
 
    //free(str2);    
    free(str3);    
 
    system("pause");
    return 0;
}


 

1. const 초기화 방법

 

상수는 선언과 동시에 반드시 초기화 해주어야 합니다. 그렇지 않으면 쓰레기 값으로 초기화 될테고 한번 초기화가 되었으므로, 생성자를 통한 초기화는 더 이상 허용되지 않습니다.

 

그래서 멤버 이니셜라이져 라는 문법이 제공된다.

 

const int id;
int age;
Person(int _id, int _age) : id(_id), age(_age) //=> 생성자 몸체보다 먼저 실행,

{
    this->id = _id; //=> 생성자가 호출될때 초기화
}

 

 

2. const 멤버 함수
const로 생성한 함수내에서는 아래의 규칙을 따른다.

- 멤버 변수의 값 변경 허용 안됨
void show() const{
    age = 30; // 컴파일 에러, 변경 불가함
}

- 멤버 변수 값의 변경에 대한 기회제공도 불가
int* getPtr() const{ // => const int* getPtr* const { 로 고치면 정상
    return &age;    // 컴파일 에러, 주소를 전달하면 기회가 제공되므로 에러가 발생
}

void show() const{
    log(); // 컴파일 에러 발생, 해당 log함수내에서 age 변수를 조작할 가능성이 있기때문에
           // 에러가 발생한다. 만약 고치려면 log 함수도 const가 되어야 한다.
}

void log() {
   cout<<"age의 값="<<age<<endl; 
}


3. const 객체
- 데이터의 변경이 허용되지 않는 객체
- const 함수 이외에는 호출 불가
const Person p;
p.log(); // 이 log함수는 반드시 const로 선언되어 있어야 한다.

4. const와 함수 오버로딩
const도 함수 오버로딩에 포함된다.
즉 void log() const;
void log(); 
두 메소드를 작성해도 된다.

 

5. static 과 클래스

첫째, main 함수가 호출되기도 전에 메모리 공간에 올라가서 초기화 된다. 따라서 public으로 선언이 된다면, 객체 생성 이전에도 접근이 가능하다.

둘째, 객체의 멤버로 존재하는 것이 아니다. 다만 선언되어 있는 클래스 내에서 직접 접근할 수 있는 권한이 부여된 것이다.

 

static 멤버는 위와같은 특징을 지닌다.

 

static 멤버의 초기화는 생성자가 적절하다 생각하겠지만, 이는 적절하지 않다. 왜냐하면 생성자는 객체 생성시 호출되지만, static 멤버는 main함수가 호출되기도 전에 초기화되어야 하기 때문이다.

 

그래서 static 멤버 초기화 문법적요소를 제공한다.

 

class Person {
   static int count;
}
int Person::count=0; // 반드시 이렇게 초기화 해야한다.

* static 멤버의 특징
- 클래스 변수, 클래스 함수라 한다.
- main 함수 호출 이전에 메모리 공간에 올라가서 초기화 한다.(전역함수와 동일)
- 선언된 클래스의 객체내에 직접 접근 허용
- static 멤버 초기화문으로 초기화 해야한다.

 

 

6. 클래스 멤버로 상수를 정의하는 경우

class SoSimple

{

   public:
      const static int a=0;

      const static int b=10;
};

 

cout<<SoSimple::a<<endl;

cout<<SoSimple::b<<endl;

 

이렇게

 

const static으로 선언된 변수는 선언과 동시에 초기화가가능합니다.


정리중...  

 

 

#include <stdlib.h>

int **array1 = malloc( row * sizeof(int *));
for (i = 0; i < row; i++)
array1[i] = malloc( col * sizeof(int));

또는

int **array2 = malloc( row * sizeof(int *));
array2[0] = malloc( row * col * sizeof(int));
for (i = 1; i < row; i++)
array2[i] = array2[0] + i * col;

+ Recent posts