티스토리 뷰

728x90
반응형

https://www.youtube.com/watch?v=cz7LH28nomc

위 널널한개발자 님의 C++ 강의를 듣고, 저의 생각을 정리한 포스팅 입니다.

1. 왜 C++랑 Python을 비교하는가?

둘다 Base가 C 이기 때문입니다(CPython 한정).

물론, C++은 C와 Base가 유사하지, C와 완전 같은 compiler를 쓰진 않습니다.

그래도, 두 언어의 Base가 C라는 유사점, C의 생산성을 높인 스타일을 언어라는 점에서 비교하면 좋겠다고 생각이 들었습니다.

2. C++의 생성자

C++은 C언어와 동일하게 포인터를 사용합니다.

때문에, Class 멤버역시 포인터로 선언이 가능합니다.

이때, 이 포인터형식의 Class멤버일 경우 문제가 발생할 수 있습니다.

아래 예제를 보시죠

#include <iostream>

using namespace std;

class temp_cls
{
    public:
    temp_cls(){
        pro_a = new int(5);
        printf("temp object created\n");
    }
    ~temp_cls(){
        delete pro_a;
        printf("temp object removed");
    }
    int *pro_a;
};

int main()
{
    temp_cls test;
    temp_cls *temp = &test;
    temp_cls *temp2(temp);
    cout << (*temp).pro_a << endl;
    cout << (*temp2).pro_a << endl;
    delete temp;
    printf("test val %p",(*temp2).pro_a);
    return 0;
}
/*
temp object created
a.b와 b.b가 동일한 위치를 Pointing하고 있는것을 볼 수 있습니다
0x56502926aeb0
0x56502926aeb0
double free or corruption (out)
*/

C++ 는 object2(object1)와 같은 방식으로 복사를 지원합니다.

이때 object2는 별도의 복사생성자가 없다면 object1의 Class Properties를 모두 복사해 옵니다.

대신, C++의 Property의 type은 무시하고 그냥 복사를 하기 때문에, Pointer의 경우 얕은복사가 발생합니다.

따라서 temp가 삭제되면서 temp.pro_a의 할당이 해제되고, 이후에 temp2.prob에 접근하게 된다면 delete된 pro_a를 한번더 메모리해제가 일어나서 메모리 접근에 오류가 발생합니다.
(위 처럼 따로 접근을 하지 않더라도, main function이 끝나면서 temp할당 해제 -> temp2할당 해제 과정에서 역시 메모리 접근 오류가 발생합니다)

그래서, C++같은 경우는
1. 일반 생성자
2. 복사 생성자(C++의 복사 syntax시 행동할 함수)
3. 대입 연산자(복사생성자와 유사하나, 복사생성자를 구현한다면 무조건 구현해줘야됨)
4. 변환 생성자(생성자가 매개변수를 1개만 받는 경우->요 경우 해당 매개변수 1개만 넣어주면 자동으로 형변환이 일어나게 설정되는 것이기 때문에 변환 생성자라고함. 좀 내용이 복잡한데, 아래 IBM의 공식문서를 읽으시면 이해가 편합니다.)
https://www.ibm.com/docs/en/i/7.1?topic=only-conversion-constructors-c

와 같이 다양한 생성자를 정의해 줘야 합니다.

아래의 예시를 보시면 빠르게 이해가 갈 것이라고 생각합니다.

#include <iostream>

using namespace std;

class temp_cls
{
    public:
    temp_cls(void){
        pro_a = new int(5);
        printf("temp object %p created\n", this);
    }
    //int value를 받을 때 대응될 변환생성자
    explicit temp_cls(int a){
        this->pro_a = new int(a);
        printf("temp object %p created by trasnform init\n", this);
    }
    //clss object를 받을 경우의 복사생성자
    explicit temp_cls(const temp_cls &tg_obj){
        printf("temp object %p created\n", this);
        printf("receive temp_cls object. tg_obj a : %d\n", *tg_obj.pro_a);
        this->pro_a = new int (*tg_obj.pro_a);
    }
    //class pointer를 받을 경우의 복사생성자
    explicit temp_cls(temp_cls *tg_obj){
        printf("temp object %p created\n", this);
        printf("receive temp_cls pointer. tg_obj a : %d\n", *(*tg_obj).pro_a);
        this->pro_a = new int(*(tg_obj->pro_a));
    }
    ~temp_cls(){
        delete pro_a;
        printf("temp object %p removed\n", this);
    }
    //class object간 =연산을 통해서 복사할 경우
    temp_cls &operator =(const temp_cls &tg_obj){
        printf("temp object %p created\n", this);
        printf("receive temp_cls object by operator '='. tg_obj a : %d\n", *tg_obj.pro_a);
        this->pro_a = new int (*tg_obj.pro_a);
        return *this;
    }

    int *pro_a;
};

int main()
{
    temp_cls test123;
    temp_cls test(10);
    temp_cls test_op(30);
    test_op = test;
    temp_cls *temp = new temp_cls(test);
    temp_cls *temp2 = new temp_cls(temp);
    cout << (*temp).pro_a << endl;
    cout << (*temp2).pro_a << endl;
    delete temp;
    printf("test val %p is alive \n",(*temp2).pro_a);
    return 0;
}
/*
(test123 obj)temp object 0x7ffe49d54740 created
(test obj)temp object 0x7ffeb6649a68 created by trasnform init
(test_op obj)temp object 0x7ffeb6649a70 created by trasnform init
(test_op obj =operation)temp object 0x7ffeb6649a70 created
(test_op obj =operation)receive temp_cls object by operator '='. tg_obj a : 5
(temp pointer)temp object 0x55954cd76320 created
(temp pointer)receive temp_cls object. tg_obj a : 5
(temp2 pointer)temp object 0x55954cd76360 created
(temp2 pointer)receive temp_cls pointer. tg_obj a : 5
0x55954cd76340
0x55954cd76380
(temp pointer)temp object 0x55954cd76320 removed
(temp2 pointer)test val 0x55954cd76380 is alive 
(test pointer)temp object 0x7ffeb6649a70 removed
(test_op pointer)temp object 0x7ffeb6649a68 removed
*/

 

3. Python을 Class 생성자

Python의 경우 다양한 생성자를 지원하지않고, 매우 단순한 생성자만 지원합니다.

class temp_cls:
    def __init__(self, a=None, b=None):
        self.a = a
        self.b = b

import copy
if __name__ == "__main__":
    temp = temp_cls(1, [20,54,62,3,9])
    #C++의 temp_cls temp2(temp);
    #를 얕은복사 형식으로 사용한것과 같은 기능을 합니다.
    temp2 = copy.copy(temp)    
    print(temp is temp2)
    print(temp.b is temp2.b)
    del temp
    print(temp2.b)
'''
temp과 temp2가 같은지 여부 : False
temp.b와 temp2.b가 같은지 여부 : True
temp를 삭제한 뒤 temp2.b : [20, 54, 62, 3, 9]
'''

차이점이 보이시나요?

파이썬의 경우 복사생성자가 따로 존재하지 않지만, copy 라는 내장 라이브러리를 통해서 동일한 기능이 구현 가능합니다.

이때, Python과 C++의 차이점이 발생합니다.

Python의 List객체는 C++의 포인터처럼 Mutable하고 해당 주소를 통해 접근하는 객체 입니다.

위 예제에서 보면 temp2가 temp을 복사해서 받으면서 temp.b와 temp2.b가 바라보는 객체는 같은 걸 알수 있습니다.

만약 C++이었다면 위 상황에서 temp를 delete해서 날려버린다면?

temp.b역시 메모리가 해제되고, temp2.b로 접근할 때 메모리 접근 오류가 발생할 것입니다.

하지만, Python의 Garbage Collection 덕분에 해당 변수는 아직 Reference Count가 남아있고,

temp2가 삭제되기 전까지 무사히 유지될 수 있습니다.

덕분에 C++에서 존재하는 여러가지 생성자를 프로그래머가 고민할 필요 없이 사용이 가능한 것이라고 볼 수 있습니다.

728x90
반응형

'C언어 잡기술 > C++' 카테고리의 다른 글

상속에 관해서  (0) 2022.05.19
C++에는 있고, C언어에는 없는것  (0) 2022.05.14
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
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
글 보관함