C/C++ 관련 헷갈리기 쉬운 것 정리3(저장용,메모용, 윤성우 열혈 C++ 프로그래밍 정리 CH 4)

RisingJade의 개발기록·2022년 2월 15일
1

1. 정보 은닉 (Information Hiding ,CH-04-01)

팁: 프로그래머의 실수에 대한 대책이 준비되어 있어야 한다.
-> 제한된 방법으로의 접근만 허용을 해서 잘못된 값이 저장되지 않도록 도와야 하고, 또 실수를 했을 때, 실수가 쉽게 발견되도록 해야 한다.

  • 변경되기 쉬운 멤버변수를 private으로 선언하고, 해당 변수에 접근하는 함수를 별도로 정의해서, 안전한 형태로 멤버 변수의 접근을 유도하는 것이 바로 '정보 은닉'이며, 이는 좋은 클래스가 되기 위한 기본조건이 된다.

  • const 함수

    • int GetX() const;와 같이 const 선언이 추가된 함수들은 class안 쪽의 맴버변수의 값을 변경하지 않겠다는 선언이다.

    • const 함수 내에서는 const가 아닌 함수의 호출이 제한된다.

    • 함수의 매개변수로 const 참조자가 있는 경우 이 const 참조자를 대상으로 값의 변경 능력을 가진 함수 호출을 허용하지 않는다.(실제 값의 변경여부에 관계없이)

    • EX)

      class EasyClass
      {
      	private:
          	int num;
          public: 
          	int GetNum()
              {
              	return num;
              }
      };
      class LiveClass
      {
      	private:
          	int num;
          public:
          	void InitNum(const EasyClass &easy){
              	num = easy.GetNum(); // 컴파일 에러 발생! -> const 참조자가 붙은 매개변수(객체)는 const 함수 호출만 가능하다! 
                  					 // 맴버가 변경되면 안되기 때문!
              }
      }
      

    2. 캡슐화(Encapsulation, CH-04-2)

  • 캡슐화란 관련 있는 함수와 변수를 하나의 클래스 안에 묶는 것이다.

  • 캡슐화는 하나의 클래스로만 모든 것을 구성해야 하는 것은 아니고, 다른 클래스를 활용해도 된다.

  • 어떻게 구성을 하느냐가 아니고, 어떠한 내용으로 구성을 하느냐에 있다.

  • 캡슐화는 어려운 개념으로, 캡슐화의 범위를 결정하는 일이 쉽지 않기 때문


    3. 생성자와 소멸자(Constructor & Destructor, CH-04-03)

  • 생성자의 이해

    • 클래스의 이름과 함수의 이름이 동일하다.
    • 반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다.
  • 위와 같은 유형을 생성자 (constructor)라 하며, 객체 생성시 딱 한번 호출된다.

  • 보통 함수의 원형은 전역적으로(함수 밖에) 선언하지만, 함수 내에 지역적으로 선언이 가능하다.

    class SimpleClass {
    private:
    	int num1;
    	int num2;
    public:
    	SimpleClass(int n1, int n2) {
    		num1 = n1;
    		num2 = n2;
    	}
    	int GetNum1() const {
    		return num1;
    	}
    	void showData() const {
    		cout << num1 << ' ' << num2 << endl;
    	}
    };
    int main(int argc, char** argv)
    {
    	SimpleClass sc1();// 함수 원형 선언!
    	SimpleClass mysc = sc1();
    	mysc.showData();
    	return 0;
    }
    SimpleClass sc1() {
    	SimpleClass sc(20, 30);
    	return sc;
    }
    
  • 따라서, class 객체 생성시 매개변수가 없는 생성자일지라도 함수 원형 선언과 구분되게 하기 위해 SimpleClass sc()와 같은 형태는 지원이 안된다. SimpleClass sc 형태로 만들자

    • 맴버 이니셜라이저(Member Initializer)를 이용한 맴버 초기화

      class Point {
      public:
      	int x;
      	int y;
      	Point(const int& x1, const int& y1) {
      		x = x1; y = y1;
      	}
      };
      class  Rectangle
      {
      public:
      	Rectangle(const int& x1, const int& y1, const int& x2, const int& y2);
      	void ShowRecInfo() const;
      private:
      	Point upLeft;
      	Point lowRight;
      };
      Rectangle:: Rectangle(const int& x1, const int& y1, const int& x2, const int& y2)
      	 :upLeft(x1, y1), lowRight(x2,y2) // 맴버 이니셜라이져를 이용한 경우
      {
      	 //empty
      }
      
    • 위의 :upLeft(x1, y1), lowRight(x2,y2)가 의미하는 바는 아래와 같다.

      • 객체 upLeft 생성과정에서 x1, y1을 인자로 전달받는 생성자를 호출하라
      • 객체 lowRight 생성과정에서 x2, y2을 인자로 전달받는 생성자를 호출하라
    • 멤버 이니셜라이져를 이용해서 변수 및 const 상수(변수) 초기화가 가능하다.
      ex)

      class Point {
      public:
      	const int x;
      	int y;
      	Point(const int& x1, const int& y1) : x(x1)//:x(x1)은 멤버 이니셜라이저를 통해 선언과 동시 초기화가 이뤄지는 바이너리 코드로 짜여져 
       											  //int x = x1과 같은 형태가 된다. 따라서 생성자에서 초기화가 가능하다!
          {
      	//x = x1; -> x가 const 상수 이므로 이 구문은 컴파일 에러가 뜬다!
            y = y1;
      	}
      };
      
    • 아래의 이유로, 되도록이면 멤버 변수 초기화에 있어서는 생성자의 몸체가 아닌 멤버 이니셜라이저를 사용하라.

      • 초기화의 대상을 명확히 인식할 수 있다.
      • 성능의 이점이 있다.
      • 선언과 동시에 초기화가 이루어지는 변수에 대해 생성자 초기화가 가능하다 (ex. const변수, 참조자)
  • 객체의 생성과정

    1. 메모리 공간의 할당
    2. 이니셜라이저를 이용한 맴버변수(객체)의 초기화
    3. 생성자의 몸체부분 실행
    • 이때 생성자를 정의 하지 않았어도 '디폴트 생성자'라는 게 자동으로 삽입되어 호출된다.
  • 디폴트 생성자(Default Constructor)

    • 객체가 되기 위해서는 반드시 하나의 생성자가 호출되어야 한다.
    • 생성자가 정의되어 있지 않는 클래스는 C++ 컴파일러에 의해 디폴트 생성자라는 것이 자동으로 삽입된다.
    • 단, new 연산자가 아닌 malloc으로 객체를 동적 할당 하는 경우 클래스의 크기정보만 바이트 단위로 전달되기 때문에 생성자가 호출되지 않는다.
    • 아무 생성자가 하나라도 있으면 디폴트 생성자는 생성되지 않는다.
  • private 생성자

    • 클래스 내부에서만 객체를 생성한다면, 생성자를 private으로 만들어둔다.
    • 주로 객체의 생성방법을 제한하고자 하는 경우에는 매우 유용하게 쓰인다.
  • 소멸자의 이해와 활용

    • 객체생성시 반드시 호출되는 것이 생성자라면, 객체소멸시 반드시 호출되는 것은 소멸자이다. 소멸자는 다음의 형태를 갖는다.
    • 클래스의 이름 앞에 ~가 붙은 형태의 이름을 갖는다.
    • 반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다.
    • 매개변수는 void형으로 선언되어야 하기 때문에 오버로딩, 디폴트 값 설정 등이 불가능하다.
    • 소멸자에는 보통 생성자에서 할당한 메모리 공간(주로 동적할당 한 것들)의 소멸에 대한 코드가 있다.

클래스와 배열 그리고 this 포인터(CH-04-04)

  • this
    • 객체 자신을 가리키는 용도로 사용되는 포인터.
  • Self Reference
class SimpleClass {
private:
	int num1;
	int num2;
public:
	SimpleClass(int n1, int n2) {
		num1 = n1;
		num2 = n2;
	}
	int GetNum1() const {
		return num1;
	}
	void showData() const {
		cout << num1 << ' ' << num2 << endl;
	}
	SimpleClass& adder(int n) { //이런식으로 자기 자신에 대한 참조자를 반환 가능하다.
		num1++;
		return *this;
	}
};
  • 참조의 정보(참조 값)에 대한 이해
    잠깐 코드를 참조자 관련 코드를 보자
int main(){
	int num = 8;
    int &ref = num;
}

이때, ref에는 num의 정수값이 대입되는 것이 아니다.

변수 num을 참조 할 수 있는 참조의 정보가 전달된다.
즉, 변수 num을 참조할 수 있는 참조 값이 참조자 ref에 전달되어, ref가 변수num을 참조 할 수 있게 된다.

profile
언제나 감사하며 살자!

0개의 댓글