면접 대비 CS 질문 요약 (38~43)

나무에물주기·2023년 1월 6일
1
post-thumbnail

38. 재귀함수를 사용한 연결리스트 구현

#include <iostream>

#define MAX 10

using namespace std;

typedef struct Node
{
    int data;
    struct Node* Next;

}    Node;

Node* Insert(Node* node, int data) // 데이터 추가
{
    if (node == NULL)
    {
        Node* newNode = new Node();
        newNode->data = data;
        newNode->Next = NULL;

        return newNode;
    }
    node->Next = Insert(node->Next, data);
    return node;
}

Node* Search(Node* node, int data) // 데이터 탐색
{
    if (node == NULL)
    {
        return NULL;
    }
    if (node->data == data)
    {
        return node;
    }
    return Search(node->Next, data);
}

Node* Delete(Node* node, int data) // 데이터 삭제
{
    if (node == NULL)
    {
        return NULL;
    }
    if (node->data == data)
    {
        Node* nextNode = node->Next;
        free(node);

        return nextNode;
    }
    node->Next = Delete(node->Next, data);
    return node;
}

void Print(Node* node)
{
    if (node == NULL)
    {
        return;
    }

    cout << node->data;
    Print(node->Next);
}

int main()
{
    Node* head = NULL;

    // 데이터 추가
    for (int i = 0; i < MAX; i++)
    {
        head = Insert(head, i);
    }
    cout << "결과 : ";
    Print(head);

    cout << '\n';

    // 데이터 탐색
    cout << "결과 : ";
    Node* someNode = Search(head, 5);
    if (someNode != NULL)
    {
        cout << someNode->data << '\n';
    }
    
    // 데이터 삭제
    cout << "결과 : ";
    head = Delete(head, 4);
    Print(head);

    return 0;
}

39. 코드를 보고 Setter Getter를 사용함으로써 얻을 수 있는 이점을 객체지향의 특성을 바탕으로 설명


class monster
{
public:
    monster(const string name);
    void SetHealthPoint(int HealtPoint);

private:
    string mName;
    int mHealthPoint;
    int mMaxHealthPoint;
};
  • 객체지향의 4대 특성중 캡슐화에 대한 내용이다
  • 캡슐화는 "민감한" 데이터를 사용자에게 비노출한다는 의미이다
  • 캡슐화를 위해,클래스 안 데이터의 직접 수정을 막고 메서드로만 변경을 허용한다.
    1. 클래스 안 속성(=변수)을 private으로 설정. (private 수정자 :동일 클래스에서만 접근 가능)
    2. 2. private 설정된 속성에 접근 방법 : public 설정된 get/set 메서드 통해서만 허용.

properties (속성) - get/set 메서드

  1. 클래스 안 필드 private 설정 시 해당 클래스에서만 접근 가능한데, public 설정된 properties (속성) 이용 시 외부에서도 접근 가능함.
  1. properties (속성)은 변수와 메서드가 결합된 것으로서, 이 안에 필드값 얻거나 설정하는 get /set 메서드가 있음.
  1. get 메서드 : 필드 값 반환.
    set 메서드 : 필드 값 설정.
  1. Properties (속성)은 필드명과 동일하게 하되, 첫 글자는 대문자로 입력.
[예제]


class Hz

{

  private string host; // field



  public string Host   // property

  {

    get { return host; }   // get 메서드

    set { host = value; }  // set 메서드

  }

}

캡슐화 장점

  1. 클래스 안 속성/메서드에 대한 더 나은 제어. 즉, 누군가에 의해 코드가 엉망이 될 가능성 ↓.
  1. 클래스 안 필드는, 읽기전용/쓰기전용으로 설정 가능.

    ※ 읽기 전용 : get 메소드만 사용.
    ※ 쓰기 전용 : set 메소드만 사용.

  1. 유연성↑. (즉, 다른 부분에 영향 안 주고, 코드 일부만 변경 가능.)

  2. 데이터 보안 강화.

40. 문자열을 뒤집는 함수인 void Reverse string(char* str)를 작성하시오

#include <iostream>

#define MAX 101

using namespace std;

void ReverseString(char* str)
{
	int n = strlen(str);

	for (int i = 0; i < n / 2; i++)
	{
		int tmp = str[i];
		str[i] = str[n - i - 1];
		str[n - i - 1] = tmp;
	}
	cout << str;
}

int main()
{
	char c[MAX];
	cin >> c;

	ReverseString(c);

	return 0;
}

41. 명시적 캐스팅과 암시적 캐스팅에 대해 아는데로 설명하고 C++에는 어떤 캐스팅이 있는지 대략적으로 설명하시오

  • 캐스팅에는 암시적 캐스팅과 명시적 캐스팅이 있다. 암시적(Implicit) 캐스팅은 컴파일러가 형을 변환해 주는 것을 말한다. 단, 형 변환이 허용이 되고 프로그래머가 명시적으로 형 변환을 안 할 경우에 해당된다.
int number1 = 3;
long number2 = number1; // 암시적 캐스팅
  • 명시적(Explicit) 캐스팅은 프로그래머가 형 변환을 위한 코드를 직접 작성하는 것을 의미한다. C++의 캐스팅은 다음과 같은 것들이 있다. 기존의 C 스타일 캐스팅은 아래의 4가지 캐스팅 중 하나를 했었고 컴파일러가 명확하게 잡지 못한다는 문제가 있어서 아래처럼 세분화 하게 되었다.
static_cast
const_cast
dynamic_cast (C++98, 모던 C++)
reinterpret_cast

static_cast

정적 캐스트는 값에 쓰일 수도, 개체에 쓰일 수도 있다. 값에 쓰일 경우 두 숫자 형 간의 변환이며 값을 유지한다. (반올림 오차는 제외) 하지만 이진수 표기는 달라질 수도 있다.

float num1 = 3.f;
int num2 = static_cast<int>(num1);

reinterpret_cast

C++에서 가장 위험한 캐스팅 중 하나이다. 이 캐스팅이 위험한 이유는 다음과 같다.

전혀 연관이 없는 두 포인터 형 사이의 변환을 허용한다. (ex. Cat <-> House, char <-> int)
포인터와 포인터 아닌 변수 사이의 형 변환을 허용한다. (ex. Cat* <-> unsigned int)
정적 캐스팅과 다른 점은, 리인터프리터 캐스트는 이진수 표기가 달라지지 않는다는 점이다.

int* signedNumber = new int(-10);
unsigned int* unsignedNumber = reinterpret_cast<unsigned int*>(signedNumber);

const_cast

const_cast는 형을 바꿀 수는 없다. 오직 const나 volatile 어트리뷰트를 제거할 때 사용한다.

Animal* myPet = new Cat(2, "Owen");
const Animal* petPtr = myPet;

Animal* myAnimal1 = (Animal*)petPtr; // OK
Cat* myCat1 = (Cat*)petPtr; // OK

Animal* myAnimal2 = const_cast<Animal*>(petPtr); // OK
Cat* myCat2 = const_cast<Cat*>(petPtr); // Compile Error, static_cast를 사용해야 함

const_cast는 포인터 형에서만 사용이 가능하다. 값 형은 언제나 복사가 되기 때문이다. 그리고 const_cast를 코드에서 쓰려고 한다면 이는 문제가 있는 것이다. 이 캐스팅을 쓸 때는 서드파티 라이브러리에서 const를 제대로 사용하지 않았을 때와 같은 경우만 사용하는 것이 바람직하다.

dynamic_cast

동적 캐스트는 실행 중에 형을 판단한다. 그리고 포인터나 참조 형을 캐스팅 할 때만 쓸 수 있다. 또한 호환되지 않는 자식형으로 캐스팅을 하려 하면 NULL을 반환한다. 따라서 dynamic_cast가 static_cast보다 안전하다.

하지만 이 캐스팅을 쓰려면 컴파일 중에 RTTI(실시간 타입정보, Real-Time Type Information)를 켜야 한다. 참고로 이 옵션은 C++ 프로젝트에서는 성능적인 이슈로 끄는 경우가 일반적이다. RTTI가 꺼져 있으면 dynamic_cast와 static_cast가 동일하게 동작한다.

// 동적 캐스팅(dynamic casting)
Cat* myCat = dynamic_cast<Cat*>(myPet);

42. C++클래스를 만들 때 컴파일러가 자동으로 만들어주는 함수들이 어떤게 있는지 설명하고 어떤 상황에서 컴파일러가 만들어주는 함수가 위험할지 설명하시오

기본생성자(default constructor)

복사생성자(copy constructor)

복사 대입 연산자(copy assignment operator)

소멸자(destructor)

(C++11에서 이동 생성자, 이동 대입 연산자가 추가)

이때 만들어지는 함수는 모두 기본형(default)이다. 그리고 모두 public이며 inline함수이다.

class Empty {
public:
    Empty() { ... }           // 기본생성자
    Empty(Empty& rhs) { ... } // 복사생성자
    ~Empty() { ... }          // 소멸자

    Empty& operator=(const Empty& rhs) { ... } // 복사 대입 연산자
};

암시적 생성자의 단점 : 객체가 유일하게 존재해야 하는 경우(객체의 사본이 금지될 경우)에는 암시적 생성자를 의도적으로 막는 과정이 필요함

컴파일러가 생성하는 함수는 모두 public 멤버가 된다. 복사 생성자와 복사 대입 연산자가 저절로 만들어지는 것을 막기 위해 private 멤버로 선언하면, 일단 클래스 멤버 함수가 명시적으로 선언되기 때문에, 컴파일러는 자신의 기본 버전을 만들 수 없고, 이 함수들이 비공개(private)의 접근성을 가지므로, 외부로부터의 호출을 차단할 수 있다.

그러나 private 멤버 함수는 그 클래스의 멤버 함수 및 프렌드(friend) 함수가 호출할 수 있다는 문제점이 남아있다. 이것을 막으려면 정의(define)를 안 해 버리면 된다. 정의되지 않은 함수를 누군가가 호출하려 했다면 링크 시점에 에러를 보게 된다.

43. 부모클래스 A로부터 상속된 B,C,D가 있을 때, A의 소멸자에 virtual키워드를 왜 붙여야하는지 설명하시오.

  • 자식클래스는 자신이 부모클래스로부터 상속을 받는다는 사실을 알고 있기 때문에, 자신이 소멸될 때 부모 클래스의 소멸자도 호출하게 된다 (소멸자는 생성자의 역순)
  • 하지만 부모클래스는 자신이 누구에게 상속을 해주는지 알 수 없기 때문에 먼저 소멸하게 되어버리면, 자식 클래스의 소멸자를 호출 할 수 없게 된다.
  • 따라서 부모가 되는 기반 클래스들은 반드시 소멸자를 virtual로 만들어주어야 추후에 메모리 누수 문제가 발생하지 않는다.
  • 하지만 그렇다고 무조건 virtual 키워드를 사용해서는 안되는데, virtual 키워드를 사용하게되면 가상 함수 테이블 포인터(vptr, virtual table pointer)가 생기고, 가상 함수 테이블(vtbl, virtual table)에 등록이 된다.
    이러한 이유 때문에 메모리 적으로 공간을 차지하기 때문에 불필요 한 부분에 막 virtual을 붙여서는 안된다.
profile
개인 공부를 정리함니다

0개의 댓글