본문으로 바로가기

[TIL] 함수의 오버로딩 / 생성자

category etc/C++ 2021. 5. 15. 19:47

함수의 오버로딩, 생성자 (씹어먹는 C++) 

함수의 오버로딩

 

1 단계

자신과 타입이 정확히 일치하는 함수를 찾는다.

2 단계

정확히 일치하는 타입이 없는 경우 아래와 같은 형변환을 통해서 일치하는 함수를 찾아본다.

  • Char, unsigned char, short 는 int 로 변환된다.
  • Unsigned short 는 int 의 크기에 따라 int 혹은 unsigned int 로 변환된다.
  • Float 은 double 로 변환된다.
  • Enum 은 int 로 변환된다.

3 단계

위와 같이 변환해도 일치하는 것이 없다면 아래의 좀더 포괄적인 형변환을 통해 일치하는 함수를 찾는다.

  • 임의의 숫자(numeric) 타입은 다른 숫자 타입으로 변환된다. (예를 들어 float -> int)
  • Enum 도 임의의 숫자 타입으로 변환된다 (예를 들어 Enum -> double)
  • 0 은 포인터 타입이나 숫자 타입으로 변환된 0 은 포인터 타입이나 숫자 타입으로 변환된다
  • 포인터는 void 포인터로 변환된다.

4 단계

유저 정의된 타입 변환으로 일치하는 것을 찾는다 (이 부분에 대해선 나중에 설명!) (출처)

만약에 컴파일러가 위 과정을 통하더라도 일치하는 함수를 찾을 수 없거나 같은 단계에서 두 개 이상이 일치하는 경우에 모호하다 (ambiguous) 라고 판단해서 오류를 발생하게 됩니다.

 


생성자

생성자를 지정하지 않는다면 자동으로 default 생성자가 지정되며 dump 값이 지정될 것이다. 생성자 또한 오버로딩이 가능하다.


문제 2 (링크)

수학 관련 소프트웨어를 만드는 회사에서 의뢰가 들어왔습니다. 중학생용 기하학 소프트웨워를 만드는 것인데요, 클래스는 총 두 개로 하나는 Point 로 점에 관한 정보를 담는 것이고 다른 하나는 Geometry 로 점들을 가지고 연산을 하는 클래스 입니다. 즉 아래와 같은 두 클래스의 함수들을 모두 정의하세요 (난이도 : 上)

class Point {
  int x, y;

 public:
  Point(int pos_x, int pos_y);
};

class Geometry {
 public:
  Geometry() {
    num_points = 0;
  }

  void AddPoint(const Point &point) {
    point_array[num_points ++] = new Point(point.x, point.y);
  }

  // 모든 점들 간의 거리를 출력하는 함수 입니다.
  void PrintDistance();

  // 모든 점들을 잇는 직선들 간의 교점의 수를 출력해주는 함수 입니다.
  // 참고적으로 임의의 두 점을 잇는 직선의 방정식을 f(x,y) = ax+by+c = 0
  // 이라고 할 때 임의의 다른 두 점 (x1, y1) 과 (x2, y2) 가 f(x,y)=0 을 기준으로
  // 서로 다른 부분에 있을 조건은 f(x1, y1) * f(x2, y2) <= 0 이면 됩니다.
  void PrintNumMeets();

private:
  // 점 100 개를 보관하는 배열.
  Point* point_array[100];
  int num_points;
};

풀이 코드

#include <iostream>
#include <cmath>
#include <random>
using namespace std;
class Point {
  int x, y;

 public:
  Point(int pos_x, int pos_y){
	  x = pos_x;
	  y = pos_y;
  }
	
	int getX() const {return x;}
	int getY() const {return y;}
};

class Geometry {
private:
  // 점 100 개를 보관하는 배열.
  Point* point_array[100];
  int num_points;
	
 public:
  Geometry() {
    num_points = 0;
  }
	~Geometry(){
		for(int i =0; i < num_points;i++){
			delete point_array[i];
		}
	}

  void AddPoint(const Point &point) {
    point_array[num_points ++] = new Point(point.getX(), point.getY());
  }

  // 모든 점들 간의 거리를 출력하는 함수 입니다.
  void PrintDistance(){
	  double disSum = 0;
	  for(int idx1 = 0; idx1 < num_points; idx1++){
		  for(int idx2 = idx1; idx2< num_points; idx2++){
			  disSum += sqrt((pow(point_array[idx1]->getX() - point_array[idx2]->getX(),2) + pow(point_array[idx1]->getY() - point_array[idx2]->getY(),2)));
		  }
	  }
	  std::cout << "모든 점들 간의 거리 : " << disSum << std::endl;
  }

  // 모든 점들을 잇는 직선들 간의 교점의 수를 출력해주는 함수 입니다.
  // 참고적으로 임의의 두 점을 잇는 직선의 방정식을 f(x,y) = ax+by+c = 0
  // 이라고 할 때 임의의 다른 두 점 (x1, y1) 과 (x2, y2) 가 f(x,y)=0 을 기준으로
  // 서로 다른 부분에 있을 조건은 f(x1, y1) * f(x2, y2) <= 0 이면 됩니다.
  void PrintNumMeets(){
	  // 아직 완성하지 못했다.
      // 삼중 for 문으로 하면 될거 같은데 중복되는 교점은 어떻게 처리할지..
      // 
  }

};

int main() {
    // 시드 값을 얻기 위한 random_device 생성
    std::random_device rd;

    // random_device를 통해 난수 생성 엔진을 초기화
    std::mt19937 gen(rd());

    // 0부터 99까지 균등하게 나타나는 난수 열을 생성하기 위해 균등 분포 정의
    std::uniform_int_distribution<int> dis(0, 99);

    // point 개수
    const int NUM_OF_POINT = 8;

    Geometry geometry;
    
    for (int i = 0; i < NUM_OF_POINT; i++) {
        // std::cout << "난수 : " << dis(gen) <<", " << dis(gen) <<std::endl;
        Point point(dis(gen), dis(gen));
        geometry.AddPoint(point);
    }
    
    geometry.PrintDistance();
    geometry.PrintNumMeets();

    return 0;
}

문제의 간단한 해법

 

어떤 방식으로 접근했는지 & 사용한 문제 해결 전략

 

문제의 해법을 찾는 데 결정적이었던 깨달음은 무엇이었는지

 

오류 원인 / 추가 공부

-  '.' operand 와 '->' operand 의 차이를 기억해두자.

Note that for a pointer variable x

myclass *x;

  • *x means "get the object that x points to"
  • x->setdata(1, 2) is the same as (*x).setdata(1, 2) and finally
  • x[n] means "get the n-th object in an array".

So for example x->setdata(1, 2) is the same as x[0].setdata(1, 2).

- 소멸자 사용에 대해 알아두자.

소멸자는 객체가 종료되는 경우 자동으로 호출되는 함수인데, 주로 생성자에서 동적으로 할당한 메모리를 해제하는 용도로 쓰인다. 주의해야 할 점은 함수에 매개변수로 객체를 넘겨서 처리할 때이다. 참조자로 전달하면 문제가 없으나, 단순히 객체를 전달하면 객체의 복사가 이루어지고, 해당 함수가 종료되면 해당 객체가 사라져서 소멸자가 호출된다.

- 생성자를 만들어 둔 줄 알고 진행하였더니. undefined reference to ~ 라는 error 가 발생했다. 문제의 코드를 꼼꼼히 파악하자.

unsigned int 에 대해 배웠다.

unsigned int는 양수만 저장한다. 때문에 부호에 비트를 사용하지 않아 저장 가능한 양수의 범위를 두 배로 늘인다. signed int보다 더 넓은 범위의 양수(0~4,294,967,295)를 표현할 수 있지만 실제 표현 가능한 개수는 음수를 포함한 signed int와 동일하다.

다른 사람의 코드를 보고 비교

void Geometry::printNumMeets()
{
	if (_pointNumber <= 2) return;

	int count = 0;
	int dx1, dx2, dy1, dy2;
	double tan1, tan2;
	
	for (int i1 = 0; i1 < _pointNumber - 2; i1++)
	{
		for (int i2 = i1+1; i2 < _pointNumber - 1; i2++)
		{
			dx1 = pointArray[i1]->x - pointArray[i2]->x;
			dy1 = pointArray[i1]->y - pointArray[i2]->y;
			if ((dx1 == 0) && (dy1 == 0)) // same point
			{ 
				continue;
			}
			for (int i3 = i2 + 1; i3 < _pointNumber; i3++)
			{
				dx2 = pointArray[i2]->x - pointArray[i3]->x;
				dy2 = pointArray[i2]->y - pointArray[i3]->y;
				if ((dx2 == 0) && (dy2 == 0)) continue;// same point
				else if ((dx1 == 0) && (dx2 == 0)) continue; // both vertical
				else if ((dx1 == 0) || (dx2 == 0)) count++; // one vertical
				else if ((dy1 == 0) && (dy2 == 0)) continue; // both horizontal
				else if ((dy1 == 0) || (dy2 == 0)) count++; // one horizontal
				else // neither vertical nor horizontal
				{
					tan1 = dy1 / dx1;
					tan2 = dy2 / dx2;
					if (tan1 * tan2 < 0) count++;
				}
			}
		}
	}
	std::cout << "Number of cross points: " << count << std::endl;
}

printNumMeets() 를 작성하신 다른 분의 코드이다. 구현하려는 중요한 원리는 이해하였으나, 중복되는 교점에 대해서는 처리가 없는 점을 어떻게 개선해야할지 고민해봐야겠다.

반응형