프로그램 개발/미분류

[코딩 인터뷰]지식 기반 문제 - C와 C++

(ㅇㅅㅎ) 2023. 1. 5. 14:36
728x90
반응형

 

[ 클래스와 상속 ]

 C++에서 모든 데이터 멤버와 메서드는 기본적으로 private입니다. public 키워드를 사용하면 그 값을 변경할 수 있습니다. Person이라는 class를 제작하고 Person을 상속하는 Student라는 class를 만들면 다음과 같습니다.

#include
using namespace std;

# define NAME_SIZE 50 // 매크로 정의

class Person{
    int id;
    char name[NAME_SIZE];
    
  public:
    void aboutMe(){
        count << "I am a person.";
    }
};

class Student : public Person{
  public:
    void aboutMe(){
        count << "I am a student.";
    }
};

int main(){
    Studnet * p = new Student();
    p->aboutMe();                // "I am a student" 출력
    delete p;                    // 할당 받은 메모리 반환
    return 0;
}

 

 

[ 생성자와 소멸자 ]

👀생성자

 객체가 생성되면 자동으로 호출됩니다. 사용자가 생성자를 만들거나 생성자가 정의되어 있지 않으면 컴파일러는 기본 생성자라고 불리는 생성자를 자동으로 만듭니다.

Person(int a){
    id = a;
}

// 기본형 변수 초기화
Person(int a) : id(a){
    ...
}

 위의 id(데이터 멤버)는 실제 객체가 만들어지기 전, 그리고 생성자 코드의 나머지 부분이 실행되기 전에 값을 할당받습니다. 이런 방식은 상수 혹은 클래스형 필드를 초기화할 때 유용합니다.

 

👀 소멸자

 객체가 소멸될 때 자동으로 호출됩니다. 객체를 삭제하는 작업으로 인자를 전달할 수 없습니다.

~Person(){
    delete obj;  // 클래스 안에서 할당한 메모리 반환
}

 

 

[ 가상 함수 ]

Student * p = new Student();
p->aboutMe();                // "I am a student" 출력

Person * p = new Student();
p->aboutMe();               // "I am a person" 출력

 파생 클래스에서 재정의할 것으로 기대하는 멤버 함수를 의미합니다. 위의 예제들은 가상 함수가 아니기 때문에 모두 정적 바인딩을 하게 되므로 [Person * p = new Student(); p->aboutMe();]에서 Student 클래스에서 구현된 aboutMe를 호출하지 않습니다. Student 클래스에서 구현된 aboutMe를 호출하고 싶다면, Person class의 aboutMe 메서드를 가상함수(virtual)로 선언해야 합니다.

⭐ 가상 함수의 경우 결합하는 타입이 분명할 때에는 일반 함수와 같이 정적 바인딩을 하나 기초 클래스 타입의 포인터나 참조를 통하여 호출될 때만 동적 바인딩을 하게 됩니다.

class Person{
    ...
    virtual void aboutMe(){
        count << "I am a person.";
    }
};

class Student : public Person{
  public:
    void aboutMe(){
        count << "I am a student.";
    }
};

 이 외에 부모 클래스에 어떤 메서드를 구현해 둘 수 없는(혹은, 구현하고 싶지 않은) 경우에도 가상 함수를 사용합니다.

 

 

[ 가상 소멸자 ]

 동적으로 할당된 메모리를 정상적으로 제거하기 위해서 가상 소멸자를 선언해야 합니다.

class Person{
  public:
    virtual ~Person(){
        count << "Deleting a person." << endl;
    }
};

class Student : public Person{
  public:
    ~Student(){
        count << "Deleting a student." << endl;
    }
};

int main(){
    Person * p = new Student();
    delete p;                   // 출력 : Deleting a student.\nDeleting a person.
}

 

 

[ 기본값 ]

 함수를 제작할 때 기본값은 반드시 함수 선언의 우측 부분에 설정해야 합니다.

int func(int a, int b=3){
    x = a;
    y = b;
    return a + b;
}

w = func(4);
z = func(4, 5);

 

 

[ 연산자 오버로딩 ]

 연산자 오버로딩 기능을 사용하면 +와 같은 연산자를 객체 간 연산에도 사용할 수 있습니다. 가령 두 개의 BookShelf 객체를 하나로 합치고 싶을 때, +연산자를 다음과 같이 오버로딩하면 됩니다.

BookShelf BookShelf::operator+(BookShelf &other){ ... }

 

 

[ 포인터와 참조 ]

👀 포인터

 포인터는 변수의 주소를 담는 변수입니다. 변수의 값을 읽거나 변경하는 등, 변수에 적용 가능한 연산은 모두 포인터를 통해 할 수 있습니다.

⭐ 포인터 변수의 크기는 아키텍처에 따라 달라집니다. 32비트 컴퓨터면 32비트, 64비트 컴퓨터면 64비트입니다.

int * p = new int;
*p = 7;
int * q = p;
*p = 8;
count << *q;        // 8 출력

 

👀 참조

 참조는 기존에 존재하는 객체에 붙는 또 다른 이름이며, 별도의 메모리를 갖지 않습니다.

⭐ 참조는 포인터와 달리 null이 될 수 없으며 다른 메모리에 재할당 될 수도 없습니다.

int a = 5;
int & b = a;
b = 7;
count << a;    // 7 출력

 

 

[ 템플릿 ]

 하나의 클래스를 서로 다른 여러 타입에 재사용할 수 있도록 하는 방법입니다.

template <typename T>
T add(T x, T y){
  return x + y;
}

a = add(1, 2);
b = add(0.5, 0.3);

 

반응형