오늘도 어지러운 나의 머리
정신 차리고 다시 오답을 정리해보자
1. 다음 코드의 결과는? : 컴파일 에러
#include <iostream>
class Calculator
{
public :
int add(int a, int b) { return a + b; }
int add(int a, int b, int c) { return a + b + c; }
int add(const int& a, const int& b) { return a + b; }
};
int main()
{
Calculator calc;
std::cout << "Adding 2 integers : " << calc.add(5, 10) << std::endl;
return 0;
}
int형 매개변수를 받는 함수 add가 2개가 있기 때문에 어떤 함수를 사용해야하는지 컴파일러가 혼란해 한다.
이렇게 또 한번 바보 인증을 했다. 허허....
2. 다음 중 컴파일 오류인 것을 모두 고르시오 : 1, 2
int num = 0;
//int& rNum = 100; //(1)
int&& rrNum = num; //(2)
int& rNum = num; //(3)
int* p = nullptr; //(4)
int*& pr = p; //(5)
int** pp = &p; //(6)
1번 : 값을 초기화 할 때 int 형의 100을 참조형으로 초기화 할 수 없다고 컴파일러가 말한다..
2번 : 이것은 명확하게 알고 있었다. int 참조 참조형을 int형으로 초기화 할 수 없다는 것.. 근데 왜 이것만 보고 1번을 보지 못했을까 심각한 문제로 느껴지며 자괴감이 든다. 그래도 이겨낼 것이다. 허허...
3. 다음 코드의 결과는 ? : 컴파일 에러
#include <iostream>
using namespace std;
class Point
{
int x, y;
public:
Point(const Point& p) { x = p.x; y = p.y; }
int getX() { return x; }
int getY() { return y; }
};
int main()
{
Point p1;
Point p2 = p1;
cout << "x = " << p2.getX() << "y = " << p2.getY();
return 0;
}
Point 클래스에 대한 기본생성자가 없다고 한다. 생각해보니 이 곳에서 Point class를 만들면 반드시 생성자가 필요하며, 초기 값 또한 설정해주어야 하는데 여기는 일단 냅다 선언을 하고 본다.. 또 이렇게 중요한 것을 놓치고 말았다. 코드를 진짜 열심히 읽기도 하고 보기도 많이 해야 할 것 같다.. 에휴... 못난 머가뤼...
4. 다음 코드의 결과는 ? : 16, 8
#include <iostream>
using namespace std;
class Test
{
static int x;
int* ptr;
int y;
int z;
};
int main()
{
Test t;
cout << sizeof(t) << " ";
cout << sizeof(Test*);
return 0;
}
전 회사 ptsd가 오는 공포의 static이다. 일단은 무조건 static부터 박고 시작했던 상사.. 스태틱을 써야하는 이유까지 이야기해가면서 써야한다고 억지부리던..상사.. 포인터는 8바이트라 인지하고 있었고, int형은 4바이트씩이라 인지하고 있었다. static이 붙더라도 결국 int형이니까 4바이트가 아닌가 하고 모두 더하여 20이라고 작성했다. 그에 따라서 Test의 포인터 크기는 어떻게 계산을 해야하나 고민이 많이 되었다.
static 변수는 인스턴스 크기에 포함되지 않기 때문에 연산에서 제외해주어야 한다. static 변수가 일반 변수와는 다른 공간에 저장된다는 것은 알았지만 인스턴스 크기에 포함되지 않는다는 부분까지 적용이 되는줄은 몰랐다.
클래스 포인터는 그냥 x64비트 환경이라고 교수님 께서 정의해주셨기 때문에 8바이트이다. 그런데 아주 엉뚱한 답을 해버린 나.. 정말 어리석다.. 구조체여도 결국 8바이트 크기의 포인터 변수이건만..
5.다음 코드의 결과는? : Size: 8 Size: 8 Size: 16
#include <iostream>
using namespace std;
class PureClass
{
public:
virtual void PureFunction() = 0;
};
class PureClass2
{
public:
virtual void PureFunction2() = 0;
};
class DerivedClass : public PureClass
{
public:
virtual void PureFunction() override {};
};
class DerivedClass2 : public PureClass, public PureClass2
{
public:
virtual void PureFunction() override{}
virtual void PureFunction2() override{}
};
int main()
{
std::cout << "Size: " << sizeof(PureClass) << " ";
std::cout << "Size: " << sizeof(DerivedClass) << " ";
std::cout << "Size: " << sizeof(DerivedClass2) << std::cout;
return 0;
}
가상함수도 결국 클래스 단위의 vtable의 주소를 가리키는 포인터를 갖기 때문에 8바이트 크기라고 할 수 있다. 아맞다.. 결국 가상함수도 포인터를 갖고 있기 때문에 8바이트인 것이다... 그런데 이제 DerivedClass2의 경우 가상함수 2개를 갖고 있기 때문에 16바이트가 되는 것이다. 정말 하수만도 못한 그 자체의 답안을 내어버린 나. 하지만 이걸 통해서 다시 한 번 리마인드가 되었다고 생각하면 되겠지만 일단 자괴감이 들긴 하다..껄껄.. 시험지에 비가 내린다. 집중호우다 세상에..
6. 다음 코드의 결과는 ?
constructor for id 1
constructor for id 2
constructor for id 3
destructor for id 3
destructor for id 2
destructor for id 1
#include <iostream>
using namespace std;
class A
{
int id = 0;
static int count;
public:
A()
{
count++;
id = count;
cout << "constructor for id " << id << endl;
}
~A()
{
cout << "destructor for id " << id << endl;
}
};
int A::count = 0;
int main()
{
A a[3];
return 0;
}
A형의 크기 3인 배열로 만들라구 되어있어서 생성자만 호출되나 한 멍청이는 손들고 서있어야한다. 소멸자도 자동으로 호출된다는걸 알고 있으면서 시험때 의구심이 들어버린 나는 혼나야된다. 왜 알고있는걸 의심해서 다르게 적는 걸까..심지어 생성자 적는 것도 constructor for id 1 2 3 이렇게 적었다. 그런데 일단 이제야 생각이 든 것은 id 값 하나만 호출하는건데 왜 출력 값으로 1 2 3을 적었나 이유를 모르겠다. 아마도 반복문이 없기 때문에 출력되는 숫자만 적은 것 같다. 멍청하다. id가 3개 출력된다면 당연히 그 앞에 있는 문자들도 3개 출력될텐데 말이다.
7. 다음 코드의 결과는? : 런타임 에러
#include <iostream>
using namespace std;
class Test
{
private:
int x;
public:
Test() { x = 0; }
void destroy() { delete this; }
void print() { cout << "x = " << x; }
};
int main()
{
Test obj;
obj.destroy();
obj.print();
return 0;
}
런타임 오류를 예상했다. 생성한 Test 객체를 삭제하고 print 하려면 print 할 것이 없기 때문이라고 생각했기 때문이다.

main에서 Test obj;를 통해 스택에 객체를 생성한다. 하지만 스택 객체를 delete하면 안된다고 한다. 만약 delete 하고싶다면 Test obj를 동적으로 할당시켜준 객체가 되어야 한다.




가능하다는 경우의 수로 모두 출력해보았다. 첫 번째 경우의 수가 원래 코드에서 수정한 버전이다. 출력된 값은 쓰레기값인 것 같지만 결론은 출력이 된다는 것이다. 소멸 이후에 해당 객체에 접근하면 안된다는 것은 분명히 알았다. 이렇게 또 한번 정확한 원인을 알아낼 수 있어서 기쁘다. 맨날 이런 느낌 저런 느낌 같은 말같지도 않은 소리만 듣다가 이렇게 알게 되어서 말이다. 한이 아주 맺혔다
8. 다음 중 C++의 가상 함수에 대한 설명 중 옳은 것은?
1) 가상 함수는 파생 클래스에서 같은 함수 서명으로 재정의할 수 있는 함수입니다.
2) 가상 함수는 상속 구조에서 런타임 다형성을 구현하는 수단입니다.
3) 베이스 클래스에서 가상으로 선언된 함수는 포인터나 참조 변수의 선언 타입과 관계없이 실제 객체의 타입에 맞는 구현이 호출됩니다. 반면 일반 함수는 포인터나 참조 변수의 선언 타입에 따라 호출됩니다.
4)위의 모든 사항
1)번과 2)번 항목은 맞다고 생각이 들었지만 3번 항목은 나에게 애매하게 느껴졌다. 교수님께서 다형성 공부해오라고 하셨는데 또 다형성과 관련된 항목에서 틀린 듯 하다. 마음이 조급해서 정확한 말 뜻을 이해하는데 오래걸려 패스한 것도 있다.
가상함수 받을 때는 객체 타입에 맞춰서 호출해야한다. 내가 배웠던 내용을 좀 더 차분히 생각했어야했는데 아쉽다. 일반 함수를 받을 때는 변수가 선언된 타입에 맞추어 정의된 함수가 호출된다. 그렇다면 아주 맞는 문장이라고 할 수 있다고 바로 이해할 수 있었다. 좀 더 여유를 갖고 생각해볼껄 그랬다 싶다. 하지만 생각하는 것 또한 실력이니까 핑계를 대고 있는 것이다. 생각을 다시 바로 잡는다.
9. 다음 코드의 결과는 ? : 컴파일 에러
#include <iostream>
using namespace std;
class B;
class A
{
int a;
public:
A() : a(0) { }
void show(A& x, B& y);
};
class B
{
private:
int b;
B() : b(0) { }
friend void A::show(A& x, B& y);
};
void A::show(A& x, B& y)
{
x.a = 10;
cout << "A::a= " << x.a << " B::b = " << y.b;
}
int main()
{
A a;
B b;
a.show(a, b);
return 0;
}
일단 : 연산자는 범위연산자로 알고 있었는데 이러한 형태로 쓰였을 때 어떤 식으로 내가 이해하면 되는지 알기 어려웠다.
또한 friend를 이용해 A::show 선언을 했기 때문에 B클래스의 private멤버에 접근할 수 있고, 전역 함수로 A, B 둘 다 구현이 가능했던 것인가 생각이 들었다.
그런데 컴파일 에러 중에서도 왜 B자료형의 b 혼자 오류가 발생하는지 이해가 되지 않았다. 계속해서 코드를 읽어보았을 때 내린 결론은 int b가 private으로 접근 제한이 되어있기 때문인 것인데, 결국 B는 자료형이고 내가 B 자료형에서 새로운 변수를 생성할 수 있는 것은 아닌가에 대한 의문이 생겼다. 그렇게 테스트를 해보았는데도 컴파일 에러가 발생했다.
그래서 왜 컴파일 에러인지 파악하기 어렵다..
10. 다음 코드의 결과는? : pDrivedBB is valid
#include <iostream>
using namespace std;
class Base
{
virtual void func() { }
};
struct DerivedA : public Base {};
struct DerivedB : public Base {};
int main()
{
Base* pBase = new Base();
DerivedA* pDerivedA = new DerivedA;
DerivedB* pDerivedB = new DerivedB;
//Upcasting
Base* pBaseA = static_cast<Base*>(pDerivedA);
Base* pBaseB = static_cast<Base*>(pDerivedB);
//DownCasting
{
DerivedA* pDerivedA = dynamic_cast<DerivedA*>(pBaseA);
DerivedB* pDerivedB = dynamic_cast<DerivedB*>(pBaseB);
DerivedA* pDrivedAA = dynamic_cast<DerivedA*>(pBaseB);
DerivedB* pDrivedBB = static_cast<DerivedB*>(pBaseA);
if (pDrivedBB)
{
std::cout << "pDrivedBB is valid" << std::endl;
}
else
{
std::cout << "pDrivedBB is valid" << std::endl;
}
}
return 0;
}
타입이 달라도 결국 pBaseB, pBaseA는 Base 자료형에서 파생된 객체이기 때문에 dynamic, static casting이 가능하다는 것일까? 정확하게 무엇때문에 가능한지 잘 이해가 가지 않는다. static캐스트를 할 경우엔 타입을 체크하지 않고 강제로 캐스팅 하기 때문에 위험성이 있어 안되는 것이 맞는게 아닌가 생각하기 때문이다...
미해결된 부분까지 정리해서 오답노트를 작성해보았다. 교수님과 면담 후에 다시 게시글을 수정하여 올바르고 정확한 답변을 작성할 것이다.
'알고리즘 풀이' 카테고리의 다른 글
C++ 오답 노트 (0) | 2025.03.30 |
---|---|
블록 탑 쌓기 위한 최소 에너지 구하기 C++ (0) | 2025.03.30 |