함수의 오버로딩, 생성자 (씹어먹는 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() 를 작성하신 다른 분의 코드이다. 구현하려는 중요한 원리는 이해하였으나, 중복되는 교점에 대해서는 처리가 없는 점을 어떻게 개선해야할지 고민해봐야겠다.
'etc > C++' 카테고리의 다른 글
[TIL] STL - 벡터(vector), 리스트(list), 데크(deque) (0) | 2021.05.22 |
---|---|
[TIL] 생성자 초기화 리스트 / const, static / 난수 생성 / 레퍼런스를 리턴하는 함수 / this / const 함수 (0) | 2021.05.20 |
[TIL] C++ 참조자 / new, delete / class 객체 (0) | 2021.05.14 |