반응형

Static Function (정적 함수)

  •  .cpp 파일내에 선언 정의가 함께 있으며, .cpp 파일내에서만 사용가능

 

Static Member Function (정적 멤버 함수)

  • C++ 에서는 클래스의 멤버 함수또한 정적으로 선언 가능
  • static member function 클래스의 객체를 생성하지 않고도클래스 이름만으로 호출 가능
  • 객체를 생성하지 않으므로, this 포인터를 가지지 않음
  • 특정 객체와 결합하지 않으므로, 정적 멤버 변수 밖에 사용할 없음ex1)

static 멤버 변수의 초기화는 클래스 외부에서 한다

위 그림에서 보이는것처럼 nonstatic member function 에서는 비정적멤버 변수, 정적 멤버변수 둘다 접근 가능하다.

그러나

"비 정적 멤버 참조는 특정 객체에 상대적 이어야 합니다"

 Static Member Function(정적 멤버 함수) 에서는, 비정적멤버 변수에 접근할려고 할경우 에러가 뜬다

따라서 오직 정적 멤버 변수(static member variable)만 접근이 가능하다

 

또한 private 인경우, static 이라 할지라도, 클래스 외부에서 사용 불가능하다

private 로 바꿨더니 호출이 불가능하다

 

private:

static member function

클래스 스코프 내부에서만 사용가능

public:

static member function

클래스 스코프 내부 외부에서

객체 생성하지 않고 사용가능

static function

.cpp 파일 내에서만 사용가능, 선언과 정의
둘다
.cpp 있어야함

 

반응형

'C++' 카테고리의 다른 글

c++ Singleton Pattern 기본 구현 방법  (0) 2020.03.03
const  (0) 2020.02.29
CopyConstructor, Shallow Copy, Deep Copy  (0) 2020.02.24
std::function (c++ 11)  (0) 2020.02.23
Input Output library  (0) 2020.02.21
반응형

fseek

(스트림 위치 지정자 이동)

C

 #include <stdio.h>

C++

 #include <cstdio>

 

fseek(FILE* fileStream, long int offset, int origin);

  • origin 인자로 전달된 위치로 부터 offset 만큼 더해진 위치로 위치 지정자(poistion indicator) 이동시킴

 

origin 인자로 들어가는 매크로 상수들이 존재한다, 해당 위치로 이동하고 싶으면

fseek(pFile, 0, SEE_SET); // 시작위치로 이동

처럼 호출하면 된다

 

 

SEEK_SET

파일의 시작

SEEK_CUR

현재 파일 포인터의 위치

SEEK_END

파일의

 

 

주의 : 텍스트 파일에 fseek 함수를 사용할때, offset 인자로 0 아닌 혹은 ftell 함수에 의해 반환된 값을 사용할 때에는 일부 플랫폼에서는 문제가 생겨서, 예상치 못했던 위치로 이동할 있음

 

 

 

 

ftell

(스트림 위치 지정자의 현재 위치를 알려줌)

 

C

 #include <stdio.h>

C++

 #include <cstdio>

 

 long int ftell(FILE* stream);

  • fseek함수와 같이 써서 파일의 크기를 알아내는데 사용한다
  • 이진(binary) 스트림의 경우, 리턴된 값은 파일의 시작 부분에서 현재위치 까지의 바이트 수를 말함
  • 텍스트(txt) 스트림의 경우, 보이지 않는 문자( Line Feed , Carriage Return, ...) 등등도 포함한 바이트 수가 나오게 된다
    • LF : Line Feed, 현재 위치에서 바로 아래로 이동 \n
    • CR : Carriage Return,  커서의 위치를 앞으로 이동 \r

 

fseek + ftell 파일 크기 알아내기

 

ex1)

반응형

'C' 카테고리의 다른 글

memset, memcpy, memmove  (0) 2020.03.13
expression(표현식)  (0) 2020.03.12
Local Variable, Global Variable, Static Variable  (0) 2020.02.28
FileStream Input Output  (0) 2020.02.22
반응형

Copy Constructor (복사 생성자)

자기 자신(객체)과 같은 자료형의 instance 매개변수로 받으며 

매개변수로 받는 instance의 데이터들을 복사해서 그대로 자기자신(객체)의 데이터들에 대입하는 생성자 함수

 

어떤 클래스를 디자인하는데, Copy Constructor(복사생성자)를 구현해 놓지 않고

 

A a;

A a_2 = a;

 

위와 같이 copy initialization 경우, 컴파일러가 자동으로 default 복사 생성자를 만들어 준다

 그러나 default copy constructor Shallow Copy 동작하므로 유의하며 쓰던지, copy contructor assignment operator 따로 구현하여, 그안에서 deep copy 되도록 하여야 한다

 

 

Shallow Copy (주소값도 그대로 복사)

객체를 복사할때, default 복사 생성자를 이용할 경우, 객체안의 멤버 변수들이 전부 복사 된다 이때

변수들이 Primitive types(int, char, bool, float, double, 값형) 경우, 값들을 그대로 복사해도 문제가 되지 않는다, 그러나 class struct, 포인터 변수, 등등 객체 또는 메모리의 주소를 가리키는 경우는, 가리키는 메모리 주소를 그대로 복사하는 꼴이 되버려서, 원본 또는 복사본 둘중 하나가 delete 경우, 다른 한쪽에도 똑같은 영향을 미치는 것이다, 왜냐하면 서로 같은 주소를 바라보고 있기 때문이다

 

ex1)

 

 

코드를 보면 hello 라는 객체를 선언 한뒤 "hello" 생성자를 통해 초기화 했다

copy 라는 객체를 선언하고 hello copy initialize 했는데, 이때 copy constructor 구현되있지 않기 때문에 컴파일러는 default copy constructor 구현하여 실행시킨다

그리고 값만을 복사하는 shallow copy 진행된다

만약 primitive type 이라면 상관 없지만, 주소를 대입해야 하는 포인터

타입 이라면, 주소를 복사해버리게 된다

아래와 같이 같은 주소를 공유하게 되는 사태가 벌어지는 것이다

 

copy.m_Data = hello.m_Data;

 

 

Debug 위해서 코드를 좀더 추가해서 다시 출력해보면

역시나 둘의 주소가 같다는걸 있다.

 

 

이렇게 되면, hello.m_Data 또는 copy.m_Data 둘중 하나가 delete 되면

다른 쪽에서도 delete 된것과 같은 것이다

scope 벗어나게 되면서 copy 자동으로 소멸되고, 소멸자를 실행해

delete[] copy.m_Data; 실행해 delete[] hello.m_Data; 실행한 것과 같은 결과가 나오고

hello.GetString(); 통해 hello.m_Data 접근하여도 이미 delete 되어 메모리 반환 되었기 때문에 쓰레기 값이 나오게 된다

 

정리하자면 멤버 변수로 포인터 또는 어떤 객체의 주소를 참조하는 경우

default contructor 사용금지

Shallow Copy(값만 그대로 복사) 금지

Deep Copy 해야한다

 

 

 

 

 

Deep Copy

(주소를 새로 할당하고 새로 할당된 객체에 값들을 복사)

class struct, 포인터 변수, 등등 객체 또는 메모리의 주소를 가리키는 경우는, 가리키는 메모리 주소는 복사 하지 않고 새로 동적할당을 한다, 그리고 원본의 주소가 가리키는 값들을 새로 할당된 객체에 복사한다, copy constructor 대입연산자를 따로 구현하여 default constructor 실행되지 않게 하는 것이다

 

 

ex1) Copy Constructor 구현

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
#include <iostream>
#include <cassert>
 
class MyString
{
private:
    char* m_Data = nullptr;
    int m_length = 0;
 
public:
    MyString(const char* _source = "")
    {
        assert(_source);
 
        m_length = std::strlen(_source) + 1;
        m_Data = new char[m_length];
 
        strcpy_s(m_Data, m_length, _source);
        m_Data[m_length - 1= '\0';
    }
//Copy Constructor
    MyString(const MyString& _source)
    {
        std::cout << "User defined Copy Constructor called" << std::endl;
        m_length = _source.m_length;
        if (_source.m_Data != nullptr)
        {
            if (m_Data)
            {
                delete[] m_Data;
                m_Data = nullptr;
            }
            //Deep copy
            m_Data = new char[m_length];
            strcpy_s(m_Data, m_length, _source.m_Data);
            m_Data[m_length - 1= '\0';
        }
        else
        {
            m_Data = nullptr;
        }
    }
 
    ~MyString()
    {
        delete[] m_Data;
    }
 
    char* GetString() { return m_Data; }
    char& GetDataRef(){ return *m_Data; }
    int GetLength() { return m_length; }
};
 
int main()
{
    MyString hello("hello");
    {
        MyString copy = hello;
        char& helloDataRef = hello.GetDataRef();
        char& copyDataRef = copy.GetDataRef();
        const char* result = &helloDataRef == &copyDataRef ? "true" : "false";
        printf("Copy's &m_Data == hello's &m_Data : %s\n", result);
        printf("Copy's &m_Data : 0x%p\n"&helloDataRef);
        printf("hello's &m_Data : 0x%p\n"&copyDataRef);
    } 
 
    std::cout << hello.GetString() << std::endl;
}
cs

 

결과는

 

 

사용자가 정의가 Copy Constructor 호출됐으며

hello, copy, 각각의 데이터 주소가 별개의 주소를 가리키고 있으며

copy 소멸되었어도 hello.m_Data delete 된것은 아니기 때문에

쓰레기 값이 아닌 온전한 데이터가 출력이 된다

 

그러나 Shallow Copy

선언후 동시 초기화 뿐만 아니라

초기화만 하는 상황

 

 

코드와 같이 같은 타입의 객체를 다음 줄에 대입할때도 실행이 된다

 

MyString str2; //선언

str2 = hello; //초기화, 같은 타입 객체 대입

 

이때는 Copy Constructor 구현해놨다 하더라도 에러가 발생한다

따라서 아래처럼 대입연산자(Assignment operator)를 구현해야 한다

 

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
#include <iostream>
#include <cassert>
 
class MyString
{
private:
    char* m_Data = nullptr;
    int m_length = 0;
 
public:
    MyString(const char* _source = "")
    {
        assert(_source);
 
        m_length = std::strlen(_source) + 1;
        m_Data = new char[m_length];
 
        strcpy_s(m_Data, m_length, _source);
        m_Data[m_length - 1= '\0';
    }
 
    MyString(const MyString& _source)
    {
        std::cout << "User defined Copy Constructor called" << std::endl;
        m_length = _source.m_length;
        if (_source.m_Data != nullptr)
        {
            if (m_Data)
            {
                delete[] m_Data;
                m_Data = nullptr;
            }
            //Deep copy
            m_Data = new char[m_length];
            strcpy_s(m_Data, m_length, _source.m_Data);
            m_Data[m_length - 1= '\0';
        }
        else
        {
            m_Data = nullptr;
        }
    }
 
    //Assignment Operator
    MyString& operator = (const MyString& _source)
    {
        std::cout << "Assignment operator" << std::endl;
        //
        //if (this == &_source) //prevent self-assignment
        //    return *this;
 
        if (m_Data)
        {
            delete[] m_Data;
            m_Data = nullptr;
        }
 
        m_length = _source.m_length;
 
        if (_source.m_Data != nullptr)
        {
            m_Data = new char[m_length];
            strcpy_s(m_Data, m_length, _source.m_Data);
            m_Data[m_length - 1= '\0';
            return *this;
        }
        else
        {
            m_Data = nullptr;
            return *this;
        }
 
    }
 
 
 
    ~MyString()
    {
        delete[] m_Data;
    }
 
    char* GetString() { return m_Data; }
    char& GetDataRef(){ return *m_Data; }
    int GetLength() { return m_length; }
};
 
int main()
{
    MyString hello("hello");
    {
        MyString copy; 
        copy = hello;
        char& helloDataRef = hello.GetDataRef();
        char& copyDataRef = copy.GetDataRef();
        const char* result = &helloDataRef == &copyDataRef ? "true" : "false";
        printf("Copy's &m_Data == hello's &m_Data : %s\n", result);
        printf("Copy's &m_Data : 0x%p\n"&helloDataRef);
        printf("hello's &m_Data : 0x%p\n"&copyDataRef);
    } 
 
    std::cout << hello.GetString() << std::endl;
}
cs

 

 

//Assignment Operator 부분을 추가 시키면 다시 정상 작동된다

 

 

그런데 대입연산자(Assignment Operator) 구현할때 주의할 점은

 

 

if (this == &_source) //prevent self-assignment

return *this;

 

위코드를 반드시 붙여줘야 한다, 자기 자신을 복사하게 만드는

hello = hello;

막는 것이다, 자기 자신을 Rvalue 놓고 Lvalue 자기자신에게

대입하게 되면 Lvalue 원래 있던 자기 자신의 데이터를 지울 것이고

지우게 되면 Rvalue 데이터도 지워져서 결국 지워지는 효과가 나올 있다

(물론 의도하던 안하던, 애초에 hello = hello; 지우는 코드로 쓰는 사람은

없을 것이고, 어떤 결과가 나올지 예측하기도 힘들다)

 

어쨌든 Copy Constructor, Assignment Operator 둘다 구현해 놓는 것을 권장한다

반응형

'C++' 카테고리의 다른 글

c++ Singleton Pattern 기본 구현 방법  (0) 2020.03.03
const  (0) 2020.02.29
Static Member Function(정적 멤버 함수)  (0) 2020.02.27
std::function (c++ 11)  (0) 2020.02.23
Input Output library  (0) 2020.02.21

+ Recent posts