본문 바로가기
공부/Programming Language

C++ 2일차

by Piva 2021. 4. 30.

  어제 미처 (시간이 너무 오래 걸려서) 클래스와 오버로딩에 대한 내용을 쓰지 못했다. 오늘 조금 더 생성자와 소멸자에 대해 봤기 때문에 적기로.

(※ 개인 정리를 위한 것이므로, 이해한 내용 생략다수 + 불친절함 주의)


객체

  객체지향 프로그래밍의 핵심적인 개념.

  일단은 그 개념을 "현실 세계에 존재하는 어떠한 개념들을 표현하기 위한 데이터들의 모음(구조)" 라고 생각하고 있다.

 

  객체지향 프로그래밍을 학습할 때 있어, 중요하게 배우는 특징들이 몇 가지 있다(n년 전 자바 교과목에서 열심히 공부했었다). 여기서 되새겨보는 특징은 아래와 같았다.

 

  1. 추상화(Abstraction) : 현실의 것을 컴퓨터의 세계로 가져오는 과정에서, 불필요한 것을 없애고 중요한 것만을 남기는 과정이다. 현실 세계의 정보는 제대로 표현하기 어려운 정보도 있기 때문이다. 따라서 추상화를 사용하지 않을 경우, 현실의 모든 정보를 표현하겠답시고 객체의 구조가 매우 복잡해질 것이다.
  2. 캡슐화(Encapsulation) : 서로 연관있는 데이터와 알고리즘이 묶여있는 것을 의미한다. 이렇게 데이터들을 묶어놓으면 객체를 보호할 수 있을 뿐더러, 연관있는 데이터들 끼리 묶여있기 때문에 사용과 접근이 편해진다.
  3. 다형성 : 객체가 취하는 동작이 상황에 따라 달라지는 것을 의미한다. 이 다형성은, 뒤에 나올 오버 로딩에도 관련이 있는 특징이다.

  이 외에도 여기 안 적힌 내용은 여기에선 생략.

 

 

클래스

  클래스의 정의 또한 처음 클래스를 접했을 때와 꽤나 비슷하다. 수업에선 클래스란 "설계도"와 같은 것이라고 배웠었는데, 클래스를 설명할 때 가장 많이 비유되는 표현이 아닌가 싶다. 클래스란 객체가 갖게 될 구조를 설명해놓은 설계도와 같으며, 클래스를 통해 객체 내에 들어갈 요소를 구성한다.

 

  클래스를 통해 만들어진 객체를 '인스턴스(Instance)'라고 하며, 이렇게 생성된 인스턴스 내의 메서드나 변수를 '인스턴스 변수', '인스턴스 메서드'라고 부른다. 이는 그냥 클래스 내의 변수와 메서드를 부르는 '멤버 변수', '멤버 함수'와는 의미의 차이가 있다고 볼 수 있겠다.

 

  클래스의 구조는 다음과 같은 모양으로 만들어진다.

class Human {
	private:
    	int height;
        int weight;
        String name;
    	int age;
    public:    
        void Introduce() {
        	std::cout << "저는 " << name << "이고 나이는 " << age << "살입니다." << std::endl;
        }
};

  중간에 나오는 public과 private은 '접근 제한자'로, 클래스 내의 요소들에 대해 접근 범위를 설정하는 역할을 한다. private으로 설정되면 외부에서 접근이 제한되며, public으로 설정되면 외부에서도 해당 요소에 대해 얼마든지 접근이 가능하게 된다. 접근 제한자를 따로 명시하지 않을 경우 자동으로 private으로 설정된다.

 

 

오버로딩(Overloading)

  함수의 오버로딩은 '다중 정의'라고 표현되기도 한다. 이는 간단히 말해, '같은 이름을 가진 함수가 다수 존재해도 된다'는 것을 의미한다.

 

#include <iostream>

void foo(int x)
{
	std::cout << "int : " << x << std::endl;
}

void foo(char x)
{
	std::cout << "char : " << x << std::endl;
}

void foo(double x)
{
	std::cout << "double : " << x << std::endl;
}

  C언어에서는 같은 이름을 가진 함수가 여러 개 존재하는 것이 용납되지 않았으나, C++에서는 인자의 구성이 다르다면 가능하다. 함수 사이의 구분도 인자를 통해 이루어진다. 따라서 foo함수를 사용할 때 전달한 인자의 타입이 int형이라면 첫 번째 foo함수를 호출하게 되는 것이다.

 

  추가로 오버로딩에 해당되지 않는 경우는 아래와 같다.

  1. 반환 형식만 다르다.
  2. 호출 규칙만 다르다. : 호출 규칙에 대해선 아래 링크 참조(나도 나중에 읽어볼 것...).
    (docs.microsoft.com/ko-kr/cpp/cpp/argument-passing-and-naming-conventions?view=msvc-160)
 

인수 전달 및 명명 규칙

자세한 정보: 인수 전달 및 명명 규칙

docs.microsoft.com

 

  만약 전달받은 인자 중, 선언된 함수에 해당하는 것이 없다면 최대한 비슷한 것을 호출하게 된다. 컴파일러에서 오버로딩이 이루어지는 자세한 과정은 아래와 같다.

 

  1. 타입이 정확히 일치하는 함수를 먼저 찾는다.
  2. 타입이 정확히 일치하는 함수가 없으면 형변환을 거친 후 일치하는 함수가 있는지 찾는다.
    : 가령, float형은 double로 변환된다.
  3. 그러고도 일치하는 것이 없다면 좀더 포괄적인 형변환이 일어난다.
    : 임의의 숫자(numeric)타입은 다른 숫자 타입으로 변환된다.
  4. 유저 정의(?)된 타입 변환을 거쳐 일치하는 것을 찾는다.
  5. 여기까지도 못 찾으면 '모호하다'라고 판단되어 오류를 일으킨다.

 

생성자(Constructor)

  생성자는 일단 함수이다. 다만 '객체가 생성될 때 자동으로 호출되는 함수'로, 객체를 초기화하는 역할을 담당한다. 객체를 초기화하는 역할만을 수행하지 무언가를 반환할 필요가 없기 때문에, 생성자를 구현할 때는 반환타입을 지정할 필요가 없다.

 

클래스 이름 (인자)
{
	//초기화 내용
}
Human (int _height, int _weight, String _name, int _age)
{
	height = _height;
    weight = _weight;
    name = _name;
    age = _age;
}

  위에 적은 클래스의 생성자를 예시로 적어본다면 이런 느낌일 것이다. 생성자를 실제로 사용하는 방법은 아래와 같다.

 

Human human1(170, 70, "김", 30); //암시적 방법
Human human2 = Human(170, 70, "김", 30); //명시적 방법

  전자를 암시적, 후자를 명시적 방법이라 칭한다. 대체로 짧게 쓸 수 있다는 이점 때문에 암시적 방법을 선호한다는 듯.

 

  생성자도 함수라서 오버로딩이 가능하다. 즉, 인자만 다르게 한다면 여러 종류의 생성자를 만들 수 있으며, 객체를 다양한 방식으로 생성할 수 있게 된다.

 

 

디폴트 생성자 (Default Constructor)

  디폴트 생성자란 말그대로 '디폴트'한 생성자다. 만약 프로그래머가 클래스의 생성자를 따로 생성하지 않은 경우, 이 디폴트 생성자가 시스템 상으로 알아서 만들어져 사용된다. 클래스에 생성자가 하나라도 만들어져 있다면 디폴트 생성자는 사용되지 않는다. 위의 Human 클래스도 생성자가 따로 만들어져 있지 않기 때문에 디폴트 생성자가 사용될 것이다.

 

  명시적으로 디폴트 생성자를 만드는 방법도 있다. 아래와 같다.

class Human
{
	public:
    Human() = default;
};

 

'공부 > Programming Language' 카테고리의 다른 글

C++ 4일차  (1) 2021.05.07
C++ 3일차  (1) 2021.05.03
C++ 1일차  (0) 2021.04.28
C++ 공부  (0) 2021.04.28
Kotlin 기본 문법 - 2  (0) 2021.04.02