(C++)8장 추상화와 캡슐화, 생성자와 소멸자

C++를 포함한 객체지향 프로그래밍 언어는 추상화, 캡슐화, 은닉화, 다형성, 상속성등의 특징이 있다.

1. 추상화(abstraction)

사물을 사실적으로 표현하는 개념(구상화)이 아니라 불필요한 부분은 제거하고 공통된 특징만을 추출하여 간겨랗고 이해하기 쉽게 만드는 작업.

2. 데이터 은닉과 캡슐화

클래스의 멤버변수를 private 접근지정자를 사용하여 선언하는 것은 데이터 은닉이다. 이처럼 객체지향 프로그래밍에서 정보를 숨기는 이유는 해당정보를 수정하는 권한을 특정해서 보호하려는 의도이다.

(캡슐화)
데이터 은닉을 포함하지만 같은 개념은 아니다. 캡슐화는 객체의 속성과 기능을 하나로 묶고 세부 구현 내용은 클래스 안으로 숨기는 것을 의미한다. 즉 외부에서 객체 내부 속성에 직접 접근하거나 조작 할 수 없도록한다.
또한, 캡슐화한 클래스는 데이터와 함수가 잘 정리되어 있어 다른프로그램에서도 재활용 할 수 있다. 이때, 해당 클래스가 어떻게 구현되어있는지 복잡한 내용은 알 필요가없고 어떻게 접근해서 사용할 것인지만 알면되므로 복잡도가 줄어든다.


캡슐화가 잘 된 예제)
class Mp3
{

private:
 File file;
 PowerDevice powrdevice;
 Memory memory;

public:
 void play()
 {
   powerdevice.powerup();
   memory.expand();
   file.load();
}
};

3. 생성자, 복사생성자, 소멸자

생성자란 객체가 생성됨과 동시에 호출되는 함수
생성자 함수를 선언 할 떄는 반드시 해당 클래스의 이름과 같아야 하며, 반환형이 없어야 한다.
특징: 클래스와 같은 이름이어야 한다. 반환형이 없고 내부에 return문을 사용 할 수있다.
객체를 생성 할 때(클래스 변수 선언) 자동으로 호출된다. 주로 멤버 변수를 초기화하는 용도로 사용한다. 같은 이름으로 여러 개 만들 수있다(함수 오버로딩). 생략 할 수 있다(디폴트 생성자 자동 생성)

3-1. 디폴트 생성자

C++ 컴파일러는 기본적으로
디폴트 생성자, 복사 생성자, 소멸자, 복사 대입연산자를 자동으로 만든다.

3-2 멤버 초기화

DailyLife(~매개변수 목록~):chulsoo(~초깃값목록~),younghee(~초깃값 목록~)
{
 cout<<~생성자 시작 문자열 출력~<<endl;
}

3-3. 소멸자

클래스와 같은 이름으로 선언하되 클래스 이름 앞에 물결표(~)가 붙는다.
반환형이 없고 함수 내부에 return 문을 사용할 수 없다.
객체가 소멸할 때 자동으로 호출된다.
주로 동적메모리를 해제하는데 사용한다.
생략할수 있다(소멸자 자동생성)

#include<iostream>
using namespace std;

class Chulsoo
{
 private:
  char* name;

 public:
  Chulsoo(char* name)
{

 this->name=new char[strlen(name)+1];
 strcpy_s(this->name,strlen(name)+1,name);
 cout<<"Chulsoo::Chulsso(name)생성자 완료"<<endl;
}


void introduce();

~Chulsoo()
{
 delete[] name;
}
}

3-4 복사 생성자와 디폴트 복사 생성자

복사 생성자는 크게 3가지 경우에 호출된다.
1. 먼저 생성한 객체를 이용해서 새로운 객체를 초기화 하는 경우
2. 먼저 생성한 객체를 인자로 전달하는 경우
3. 함수 내에서 객체를 반환하는 경우

예)
Chulsoo chulsoo1(32);
Chulsoo chulsoo2(chulsoo1);
Chulsoo chulsoo2=chulsoo1; //위와 같다.

3-5 디폴트 생성자의 문제점인 얕은복사와 그 문제를 해결하는 깊은 복사

#include<iostream>
using namespace std;

class Chulsoo
{
 private:
  char* name;

public:
 Chulsoo(char* name)
{
 this->name=new char[strlen(name)+1];
 strcpy_s(this->name,strlen(len)+1,name);
}

Chulsoo()
{
 cout<<"Chulsoo::Chulsoo()생성자 완료"<<endl;
}
~Chulsoo(){
delete[] name;
cout<<"Chulsoo::~Chulsoo()소멸자 완료"<<endl;
}
};

int main(void)
{
 Chulsoo  chulsoo1("철수");
 Chulsoo chulsoo2(chulsoo1); //에러 발생

}

이는 얕은 복사의 문제점이다. 객체 chulsoo1, chulsoo2으 생성자가 두번 호출 되지만 소멸자는 한번 호출되어 에러가 발생한다.
그래서 Chulsoo(const Chulsoo& source)
{
 this->name=new char[strlen(source.name)+1];
 strcpy_s(this->name,strlen(source.name)+1,source.name);
}


2번째
먼저 생성한 객체를 인자로 전달하는 경우

int main(void)
{
 Chulsoo chulsoo1(32);
 chulsoo1.copyConstructor(chulsoo1);
 return 0;
}

class Chulsoo{

 private:
  int age;
 public:
  Chulsoo(const Chulsoo& source):age(source.age)
 {
    cout<<"복사 생성자 완료"<<endl;

 }

 Chulsoo(int age):age(age)
{
 cout<<"Chulsoo::Chulsoo(age)생성자 완료"<<endl;
}

Chulsoo(){
 cout<<"Chulsoo::Chulsoo() 생성자 완료"<<endl;
}
void copyConstructor(Chulsoo chulsooObj1){}
~Chulsoo(){}
}


3번째 함수를 반환 할 때 복사 생성자 호출

Chulsoo copyConstructorCall2()
{
 Chulsoo chulsooObj2(100);
 return chulsooObj2;
}
//함수를 반환 할 떄 복사 생성자가 호출 되는 이유는 함수의 local변수는 함수가 종료되면 소멸된다. 하지만 반환된 객체를 통해서 멤버 변수에 접근한다든지 멤버 함수를 호출 할 수 있어야 한다. 이를 위해서 C++에서는 임시 객체를만든다. 결국 chulsooObj2객체는 임시객체를 생성하면서 초기화를 수행하게 되고 이런이유로 복사생성자가 호출되는것이다.

댓글

이 블로그의 인기 게시물

(18장) WebSocekt과 STOMP를 사용하여 메시징하기

(네트워크)폴링방식 vs 롱 폴링방식

(ElasticSearch) 결과에서 순서 정렬