코딩 학습/C와 C++

C++ 배치고사 필수 문제

이개 2026. 4. 2. 14:59

1번. 배열 원소 합하기

long long sum(const vector<int>& v) {
    long long Sum = 0;
    for(int i = 0; i < v.size(); i++)
    {
        Sum+= v[i];
    }

    return Sum;
}

 

벡터 사이즈는 size() 반드시 붙여준다... 

C++은 무조건 length(), size()로 길이 구하는 건 다 함수니까 () 붙인다.

Array는 쓸 일 없으니 까먹어도 된다.

 

2번. 윤년 구하기

bool is_leap(int y) {
    if(y%4 ==0 && y%100 != 0)
    {
        return true;
    }
    else if(y%400 == 0)
    {
        return true;
    }
    else
    {
        return false;
    }

}

 

4의 배수지만 100의 배수는 아니면 true

100의 배수여도 400의 배수이면 트루

아니면 false

 

3번. 직사각형 클래스 구현하기

class Rect {
    public:
        Rect(int width, int height) : width(width < 0 ? 0 : width), height(height < 0 ? 0 : height) {}
        int area() const {
            return width * height;
        }
        void setWidth(int value)
        {
            if(value < 0)
                this->width = 0;
            else
                this->width = value;
        }
        void setHeight(int value)
        {
            if(value < 0)
                this->height = 0;
            else
                this->height = value;
        }

        private:
        int width;
        int height;
};

 

생성자 인수에서 int를 안 적을 뻔 했는데 주의하자.

 

 

4번. 은행 입출금

#include <iostream>
#include <string>
using namespace std;

class BankAccount {
private:
    string owner;
    double balance;

public:
    BankAccount(const string& name, double initial) {
        owner = name;
        if(initial >= 0)
            balance = initial;
        else
            balance = 0;
    }

    void deposit(double amount) {
        if(amount >= 0)
        {
            balance += amount;
        }
    }

    bool withdraw(double amount) {
        if (balance < amount)
        {
            return false;
        }
        else
        {
            balance -= amount;
            return true;
        }
    }

    double getBalance() const {
        return balance;
    }

    // 계좌 정보 출력하는 함수 (구현하실 필요 없음)
    void printInfo() const {
        cout << "Owner: " << owner << ", Balance: " << balance << " won\\n";
    }
};

int main() {
    BankAccount account("Alice", 1000);
    account.printInfo(); // Owner: Alice, Balance: 1000 won

    account.deposit(500);
    account.printInfo(); // Owner: Alice, Balance: 1500 won

    if (account.withdraw(2000)) {
        cout << "Withdraw OK!\\n"; // 이건 출력 안됨
    } else {
        cout << "Withdraw FAILED!\\n"; // 이것이 출력됨
    }

    if (account.withdraw(1000)) {
        cout << "Withdraw OK!\\n"; // 이것이 출력됨
    }

    account.printInfo(); // Owner: Alice, Balance: 500 won
}

 

5. 가상함수 오버라이드

#include <iostream>
#include <vector>
using namespace std;

class Animal {
  virtual void speak(){
  	cout << "???" << endl;
  };
};

class Dog : public Animal {
    public:
        void speak () override {
            cout << "Woof" << endl;
        }
};

class Cat : public Animal{
    public:
        void speak () override {
                cout << "Meow" << endl;
            }
};


int main() {
    vector<Animal*> zoo{ new Dog, new Cat, new Animal };
    for (auto p : zoo) p->speak(); // Woof / Meow / ???
    for (auto p : zoo) delete p;
}

 

가상함수 구현 안 해두면 에러남. {} 빈칸이라도 넣어놔야함.

 

6. 제네릭 함수 템플릿 구현하기

#include <iostream>
#include <string>
using namespace std;

template <typename T>
const T& get_max(const T& a, const T& b){
    return a > b ? a : b;
}

int main() {
    cout << get_max(10, 20) << "\\n";            // 20
    cout << get_max(3.14, 2.71) << "\\n";        // 3.14
    string s1 = "Apple", s2 = "Banana";
    cout << get_max(s1, s2) << "\\n";            // Banana
}

 

템플릿 함수 쓰는 법 잊지말자.

template <typename T>

그리고 이건 바로 한 파일에 구현을 해놔야한다.

 

7. 깊은 복사 생성자와 소멸자 구현

#include <iostream>
using namespace std;

class IntPtr {
    int* ptr;
public:
    IntPtr(int val) {
        ptr = new int(val);
    }

    ~IntPtr() {
        if(ptr != nullptr)
        {
            delete ptr;
            ptr = nullptr;
        }
    }

    IntPtr(const IntPtr& other) {
        if(other.ptr != nullptr)
        {
            this->ptr = new int(*other.ptr);
        }
        else
        {
            this->ptr = nullptr;
        }
    }

    int getValue() const {
        return *ptr;
    }
};

int main() {
    IntPtr p1(10);
    IntPtr p2 = p1; // 복사 생성자 호출

    cout << p1.getValue() << "\\n"; // 10
    cout << p2.getValue() << "\\n"; // 10
}

 

소멸자에서는 클래스에서 갖고 있는 포인터가 nullptr이 아니면 삭제해준다. 

 

IntPtr p1(10);

IntPtr p2 = p1; 이랑

 

IntPtr p1(10);

IntPtr p2(20);

p2 = p1;

 

이건 다른 거다.

 

🔥 1️⃣ 복사 vs 대입 차이

✅ 복사 (Copy)

👉 새 객체를 만들면서 값을 복사

 
IntPtr p1(10);
IntPtr p2 = p1; // 복사
 

또는

IntPtr p2(p1); // 복사
 

✔ 특징:

  • 새로운 객체 생성됨
  • 이때 복사 생성자 호출

✅ 대입 (Assignment)

👉 이미 있는 객체에 값을 덮어쓰기

IntPtr p1(10);
IntPtr p2(20);

p2 = p1; // 대입
 

✔ 특징:

  • 객체는 이미 존재
  • 값만 바뀜
  • 대입 연산자 (operator=) 호출

🔥 2️⃣ 한눈에 비교

구분복사대입
객체 생성 ⭕ 새로 생성 ❌ 이미 있음
호출 함수 복사 생성자 대입 연산자
코드 형태 IntPtr p2 = p1; p2 = p1;

🔥 3️⃣ 복사 생성자란?

 
IntPtr(const IntPtr& other);
 

👉 같은 타입 객체로 새 객체를 만들 때 자동 호출되는 생성자


언제 호출됨?

IntPtr p2 = p1; // ⭕
IntPtr p3(p1); // ⭕
 

👉 둘 다 복사 생성자


🔥 4️⃣ 복사 생성자는 원래 있는 거냐?

👉 있다 (컴파일러가 자동으로 만들어줌)

이걸 **기본 복사 생성자 (default copy constructor)**라고 함.


근데 문제는?

기본 복사 생성자는 이렇게 동작함:

 
this->ptr = other.ptr; // ❗ 그냥 주소만 복사
 

👉 얕은 복사 (shallow copy)


그래서 생기는 문제 💥

 
IntPtr p1(10);
IntPtr p2 = p1;
 

👉 둘 다 같은 ptr 공유

 
~IntPtr() {
delete ptr;
}
 

👉 둘 다 delete → 💥 double delete


🔥 5️⃣ 직접 만드는 이유

 
IntPtr(const IntPtr& other) {
ptr = new int(*other.ptr); // 깊은 복사
}
 

👉 각각 따로 메모리 사용 → 안전

 

8. 벡터에서 순서 유지하면서 중복 제거하기

vector<int> removeDuplicates(const vector<int>& v) {
    vector<int> Result;
    unordered_set<int> Dup;
    for (int n: v)
    {
        if (Dup.find(n) == Dup.end())
        {
            Dup.insert(n);
            Result.push_back(n);
        }
    }
    return Result;
}

 

unordered_set<int> Dup;을 써서 문제를 해결하였다. 

 

unordered_set이란?

👉 중복 없는 값을 저장하는 집합 (set)
👉 순서 없음 (unordered)
👉 해시 테이블 기반

 
#include <unordered_set>
using namespace std;

unordered_set<int> s;
 

2️⃣ 특징

✅ 1. 중복 허용 ❌

 
s.insert(10);
s.insert(10); // 무시됨
 

👉 같은 값 하나만 저장됨


✅ 2. 순서 없음

 
for (auto x : s)
cout << x << " ";
 

👉 출력 순서 랜덤 (정렬 안 됨)


✅ 3. 빠른 속도 (중요 🔥)

연산시간복잡도
insert O(1)
find O(1)
erase O(1)

👉 해시 기반이라 엄청 빠름


3️⃣ 기본 사용법

삽입

 
s.insert(5);
 

탐색

 
if (s.find(5) != s.end()) {
cout << "있다";
}
 

👉 또는:

 
if (s.count(5)) {
cout << "있다";
}
 

삭제

 
s.erase(5);
 

4️⃣ set vs unordered_set

구분setunordered_set
내부 구조 트리 (RB-tree) 해시 테이블
정렬 ⭕ 자동 정렬 ❌ 없음
속도 O(log n) O(1)
순서 있음 없음

5️⃣ 언제 쓰냐?

👉 이런 상황에서 최고 👍

  • 중복 제거
  • 빠른 존재 확인 (검색)
  • 순서 필요 없음

예:

 
unordered_set<string> visited;
 

👉 방문 체크 (그래프, BFS 등)

 

 

 

9. 두 문자열이 애너그램인지 판별하기

두 문자열이 애너그램 관계인지 판별하는 함수를 작성해주세요. 만약 애너그램이 아니라면, 한 문자열의 문자 몇 개를 바꿔야 다른 문자열과 애너그램 관계가 될 수 있을까요? 그 최소 횟수를 구해야 합니다.

  • 애너그램이면 0을 반환합니다.
  • 애너그램이 아니면 최소 변경 횟수를 반환합니다.
  • 변경: 한 문자를 다른 문자로 바꾸는 것을 의미합니다. (문자 1개 변경 = 1회)
  • 두 문자열의 길이가 다르면 1을 반환합니다.
  • 자세한 내용은 아래 코드의 main 함수 출력 예시를 참고하세요.
int getMinChangesToAnagram(const string& s1, const string& s2) {
    string s = s2;
    if(s1.length() != s2.length())
    {
        return -1;
    }

    for (int i = 0; i < s1.length(); i++)
    {
        char a = s1[i];

        size_t found = s.find(a);

        if (found != string:: npos)
        {
            s.erase(found, 1);
        }
    }

    return s.length();
}

 

const string&로 들어오는 s2는 바꿀 수 없어서 s2 따로 저장했다.

s1 글자 하나하나를 s2에서 pop 하듯이 꺼내면 남은 글자의 길이가 바꿔야하는 문자 수이다.

더 좋은 방법이 있다고 하는데 어려워서 이걸로 했다.