기말고사 범위 (24 기준)
▶Lec-9
Class
정의
클래스를 정의한다고 해서 객체가 생성되는 것은 아니다.
클래스 타입의 변수를 선언하면 해당 클래스의 객체가 생성되는데 이 과정을 인스턴스 화라고 부른다.
class Circle {
public:
double radius;
};
int main() {
Circle c; // 객체 정의 (인스턴스화))
}. : 멤버 접근 연산자
사용하여 클래스의 멤버에 접근할 수 있다.
class Circle {
public:
double radius; // 멤버
};
int main() {
Circle c; // 객체 정의 (인스턴스화))
c.radius = 3.0; //C의 멤버인 radius를 3.0으로 설정
}:: : 범위 연산자
사용하여 메서드를 클래스 외부에서도 정의할 수 있다.
class Circle {
public:
double radius;
double area();
};
double Circle::area() {
return radius*radius*3.14159;
}매서드?
멤버 변수 (Member Variable / Data Member)
클래스 내부에 선언된 변수, 객체의 상태(데이터)를 저장
class Circle {
double radius; // 멤버 변수 (속성, 데이터)
int color; // 멤버 변수
};멤버 함수 = 메서드 (Member Function / Method)
클래스 내부에 정의된 함수, 객체의 동작(기능)을 정의
class Circle {
private:
double radius; // 멤버 변수
public:
// 멤버 함수 (메서드)
double area() {
return radius * radius * 3.14159;
}
// 멤버 함수 (메서드)
void setRadius(double r) {
radius = r;
}
};클래스 멤버
private / public / protected
private : 같은 class 안에서만 접근 가능 (main에서는 접근 불가)
public : 제한 없이 외부에서도 접근 가능
protect : 해당 클래스 및 상속한 클래스에서만 접근 가능
class Circle {
private: // class 안에서만 접근 가능
double radius;
public : // class 밖에서도 접근 가능
double area() {
return radius*radius*3.141;
};
};
int main() {
Circle c;
}기본 함수
Constructor(생성자) / Destructor(소멸자)
생성자 : 새로운 객체가 생성될 때 호출, 여러 개의 생성자를 정의할 수 있으며, 각각 다른 인수들을 받을 수도 있다.
소멸자 : 객체가 소멸될 때 호출, 단 하나만 정의가 가능하다.
class Circle {
public : // class 밖에서도 접근 가능
double radius;
Circle(double r); //생성자
~Circle(); //소멸자
};
Circle::Circle(double r) {
radius = r;
}
Circle::~Circle() {
std::cout<<"bye\n";
}
int main() {
Circle c; // 객체 정의 (인스턴스화))
c.radius = 3.0;
}
1. 기본 생성자 (Default Constructor)
매개변수가 없는 생성자
class Circle {
private:
double radius;
public:
// 기본 생성자
Circle() {
radius = 1.0;
}
};
int main() {
Circle c; // 기본 생성자 호출
}2. 매개변수가 있는 생성자
class Circle {
private:
double radius;
public:
// 매개변수가 있는 생성자
Circle(double r) {
radius = r;
}
};
int main() {
Circle c(5.0); // 반지름 5.0으로 초기화
}
Getter와 Setter
private 멤버 변수에 접근하기 위한 public 메서드이다.
왜 사용하나?
- 데이터 캡슐화: private 변수에 대한 직접 접근을 막고 제어된 방식으로 접근
- 유효성 검사: 잘못된 값이 설정되는 것을 방지
- 유지보수성: 내부 구현을 변경해도 외부 인터페이스는 유지
class Circle {
private:
double radius;
public:
// Getter: 값을 반환
double getRadius() const {
return radius;
}
// Setter: 값을 설정 (유효성 검사 포함)
void setRadius(double r) {
if (r > 0) {
radius = r;
}
}
};
int main() {
Circle c;
c.setRadius(5.0); // setter 사용
double r = c.getRadius(); // getter 사용
}
▶Lec-10
inheritance
기존의 클래스를 상속받아 새로운 클래스를 생성할 수 있다.
새로 생기는 클래스(서브 클래스)는 기존 클래스(슈퍼 클래스)가 가지고 있던 요소를 모두 가지고 있다.
원래 있는 코드의 재사용을 목적으로 한다.
class Cylinder : public Circle {
private:
double height;
public:
double volume();
Cylinder(double r, double h);
};
// Cylinder 는 위의 Circle 클래스가 가지고 있던 radius 또한 포함하고 있다.Method overriding
기존의 매서드를 조정할 수 있다.
double Cylinder::area() {
return
(Circle::area()) * 2
+ (2*radius*3.14159)
* height;
}
// Circle의 area 매서드에서 Cylinder의 부피를 구하기 위한 식으로 변경서브 클래스에서 슈퍼 클래스의 생성자를 호출하는 방법
Cylinder::Cylinder(double r, double h) : Circle(r) {
height h;
}
// Circle(r)로 호출하는데 이를 초기화 리스트라고 함circle 클래스의 생성자를 호출하여 radius값을 초기화 한다. 슈퍼클래스의 생성자를 서브클래스의 생성자에서 호출할 때, 초기화 리스트를 사용해야 한다.
Polymorphism, Dynamic Binding and Heap Objects
heap 메모리 할당
객체는 new 연산자를 사용하여 힙에 할당된다.
new로 할당된 객체는 delete 연산자로 제거할 수 있다.
Circle* c = new Circle(9);
delete c;Polymorphism (다형성)
하나의 인터페이스나 기능이 여러 가지 형태로 동작할 수 있는 객체지향 프로그래밍의 주요 특성이다.
// 1. 컴파일 타임 다형성 (정적 다형성)
class Math {
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; } // 함수 오버로딩
int add(int a, int b, int c) { return a + b + c; }
};
// 2. 런타임 다형성 (동적 다형성) - Virtual Function 사용
class Animal {
public:
virtual void speak() { cout << "동물 울음 소리" << endl; }
};
class Dog : public Animal {
public:
void speak() override { cout << "멍멍" << endl; }
};
class Cat : public Animal {
public:
void speak() override { cout << "야옹" << endl; }
};
// 사용 예시
Animal* pet = new Dog(); // 부모 타입으로 자식 객체 가리킴
pet->speak(); // "멍멍" 출력 - 실제 객체 타입에 따라 동작Dynamic Binding (동적 바인딩)
프로그램 실행 중에 호출될 함수가 결정되는 메커니즘이다.
class Base {
public:
void normalFunc() { cout << "Base::normal" << endl; }
virtual void virtualFunc() { cout << "Base::virtual" << endl; }
};
class Derived : public Base {
public:
void normalFunc() { cout << "Derived::normal" << endl; }
void virtualFunc() override { cout << "Derived::virtual" << endl; }
};
int main() {
Derived d;
Base* ptr = &d;
// Static Binding (컴파일 시점 결정)
ptr->normalFunc(); // "Base::normal" 출력
// 포인터 타입(Base*)에 따라 결정
// Dynamic Binding (실행 시점 결정)
ptr->virtualFunc(); // "Derived::virtual" 출력
// 실제 객체 타입(Derived)에 따라 결정
}Virtual Function (가상 함수)
기본 클래스에서 virtual 키워드로 선언되어 파생 클래스에서 재정의할 수 있는 멤버 함수이다.
class Base {
public:
// 1. 일반 가상 함수
virtual void func1() {
cout << "Base::func1 기본 구현" << endl;
}
// 2. 순수 가상 함수 (추상 메서드)
virtual void func2() = 0; // 구현 없음, 자식이 반드시 구현
// 3. 가상 소멸자 (필수!)
virtual ~Base() {
cout << "Base 소멸자" << endl;
}
};
class Derived : public Base {
public:
// override 키워드로 명시적 재정의
void func1() override {
cout << "Derived::func1 재정의" << endl;
}
// 순수 가상 함수는 반드시 구현
void func2() override {
cout << "Derived::func2 구현" << endl;
}
~Derived() {
cout << "Derived 소멸자" << endl;
}
};정리
- Polymorphism (다형성)
- 하나의 인터페이스로 여러 구현체를 다루는 능력
- 코드의 재사용성과 확장성 향상
- Dynamic Binding (동적 바인딩)
- 실행 시점에 호출할 함수를 결정하는 메커니즘
- vtable과 vptr을 통해 구현
- Virtual Function (가상 함수)
- 동적 바인딩을 가능하게 하는 함수
- virtual 키워드로 선언, override로 재정의
Static Class Attributes
Static data members
static 키워드로 선언된 변수는 클래스의 모든 객체가 공유하는 변수이다.
static 멤버는 클래스 내에서 선언되지만, 반드시 클래스 외부에서 초기화해야 한다.
class foo{
private:
static int cnt; // static 멤버 변수
public:
foo() {
cnt++; // 생성자 호출 시 cnt 증가
cout << "there are now " << cnt
<< " foo objects" << endl;
}
};
int foo::cnt = 1; //밖에서 초기화Static methods (정적 매서드)
static 매서드는 객체 없이 호출할 수 있는 매서드이다.
매서드를 호출하기 위해서는 :: 연산자를 사용해야 한다.
class MyClass {
public:
static void staticMethod() {
std::cout << "This is a static method." << std::endl;
}
void nonStaticMethod() {
std::cout << "This is a non-static method." << std::endl;
}
};
int main() {
// 정적 메서드는 클래스 이름으로 호출
MyClass::staticMethod();
// 비정적 메서드는 객체를 생성해야 호출 가능
MyClass obj;
obj.nonStaticMethod();
}Friends Declarations
Friend 클래스는 한 클래스가 다른 클래스를 friend로 선언하면 friend 로 선언된 클래스는 해당 클래스의 private 및 protected 멤버에 접근할 수 있다.
또한 외부 함수를 friend로 선언할 수도 있다.
class foo {
private:
int i, j;
…
friend class fee; // fee 클래스를 friend로 선언
friend int printfoo(foo &f1); // 외부 함수를 friend로 선언
};this Pointer
this 포인터는 항상 객체 자신에 대한 참조를 반환한다.
Circle::Circle(double radius){
this->radius = radius; //현재 객체 내의 radius멤버변수의 값을 함수에 입력된 radius값으로 설정함
}Operator Overloading
연산자들 또한 함수처럼 클래스에서 오버로딩하여 사용할 수 있다.
ex) + 연산자 오버로딩
Circle operator+(const Circle &c1, const Circle &c2)
{
return Circle(c1.radius + c2.radius);
}
void main() {
Circle c1 = Circle(5.0);
Circle c2 = Circle(3.0);
std::cout << (c1 + c2).get_radius() << std::endl;
} //c1+c2 는 새로운 Class 객체오버로딩 할 수 있는 연산자 모음
- 단항 연산자 (Unary operators):
- +, -, *, &, ~, !, ++, --, ->
- 이항 연산자 (Binary operators):
- 산술 연산자: +, -, *, /, %
- 비트 연산자: ^, &, |
- 시프트 연산자: <<, >>
- 복합 할당 연산자: =, +=, -=, *=, /=, %=, ^=, &=, |=, <<=, >>=
- 비교 연산자: <, <=, >, >=, ==, !=
- 논리 연산자: &&, ||
- 기타 연산자: ,, [], ()
- 메모리 연산자: new, new[], delete, delete[]
▶Lec-11
Exception Handling
C++ 에서는 try, catch, throw를 통해 예외를 처리한다.
try : 예외가 발생할 가능성이 있는 코드를 감싼다.
throw : 예외를 발생시킨다.
catch : 발생한 예외를 처리한다.
#include <iostream>
using namespace std;
int divide(int a, int b) {
if (b == 0)
throw runtime_error("Division by zero!"); // 예외 발생
return a / b;
}
int main() {
try {
cout << "Result: " << divide(10, 2) << endl; // 정상 실행
cout << "Result: " << divide(10, 0) << endl; // 예외 발생
} catch (const runtime_error &e) {
// 예외 처리
cerr << "Error: " << e.what() << endl;
}
return 0;
}catch 블록의 매개변수는 다음과 같은 형태를 가짐
Multiple Exception Handlers
try에서 발생된 오류의 타입과 일치하는 catch블록에서 처리된다. 같은 타입의 catch블록은 1개씩만 존재할 수 있다. 만약 일치하는 catch가 없다면 프로그램은 강제종료된다.
try {
// 예외 발생 가능성 있는 코드
}
catch (formal parameter1) {
// 형식 매개변수1 타입의 예외 처리
}
catch (formal parameter2) {
// 형식 매개변수2 타입의 예외 처리
}
...Unhandled Exceptions
함수 내에서 처리되지 않은 예외는 메인 함수로 전파된다.
void func() {
throw 42; // 예외 발생
}
int main() {
func(); // 예외 전파
return 0;
}Nested try-catch
try-catch문 또한 중첩으로 사용할 수 있다. 안의 catch블록에서 처리되지 않은 예외는 밖의 catch블록으로 전파된다.
Rethrowing Exceptions
catch 안에서 피연산자가 없는 throw가 나타날 수 있다. 이 throw는 예외를 다시 발생시키며 예외는 다른 곳에서 처리된다.
try {
throw runtime_error("Inner exception");
}
catch (const runtime_error &e) {
cout << "Caught in innerFunction: " << e.what() << endl;
throw; // 예외를 다시 발생시킨다.
}▶Lec-12
Templates
템플릿은 특정 데이터 타입에 의존하지 않는 코드를 작성할 수 있게 한다.
또한 컴파일 타임에 데이터 타입이 결정되므로 타입의 안정성이 보장되고, 성능이 향상된다.
template <typename TYPE>
class ListOf {
public:
TYPE data; // 노드에 저장된 데이터
ListOf<TYPE> *next; // 다음 노드를 가리키는 포인터
};C++에서는 아래 두 가지 모두 허용된다.
template <class Identifier>
class_definition;
template <typename Identifier>
class_definition;템플릿은 하나 이상의 인수를 가질 수 있다.
여러 인수를 사용할 떄, 각 타입의 인수는 class 키워드로 시작하고, 쉼표로 구분한다.
template <class T1, class T2, ...>
class_definition;템플릿을 인스턴스화 하기 위해서는 다음 문법을 사용한다.
class_name<type_list>또한 템플릿을 사용하면 특정 데이터 타입에 종속되지 않는 함수를 정의할 수 있다.
template <class TP> // 또는 template <typename TP>
TP max(TP d1, TP d2) {
return (d1 > d2) ? d1 : d2;
}13, 14는 시험범위 아님 (24기준)
▶클래스 만들기
1. 클래스 기본 구조
class Course {
private: // 접근 제어자// 멤버 변수들
public:
// 멤버 함수들
};접근 제어자 (Access Modifiers)
- private: 클래스 내부에서만 접근 가능
- public: 클래스 외부에서도 접근 가능
- protected: 상속받은 클래스에서도 접근 가능 (이 코드엔 없음)
2. 생성자 (Constructor)
기본 생성자 (Default Constructor)
Course() : Id(0), Instructor(" "), RoomNr(0) {}- 매개변수가 없는 생성자
- 초기화 리스트 (
: Id(0), ...) 사용- 멤버 변수를 생성과 동시에 초기화
- 더 효율적인 방법!
매개변수가 있는 생성자 (Parameterized Constructor)
Course(int id, string instructor, int roomNr)
: Id(id), Instructor(instructor), RoomNr(roomNr) {}- 객체 생성 시 값을 받아서 초기화
- 사용 예:
Course(101, "Youngjun Won", 507)
3. Getter 함수 (접근자)
int getId() const { return Id; }const 키워드
- 함수 끝의 const: 이 함수는 멤버 변수를 수정하지 않음을 보장
- 객체의 상태를 변경하지 않는 함수에 사용
- const 객체에서도 호출 가능
const Course* getCourses() const {return Courses; }- 반환형의 const: 반환된 포인터가 가리키는 데이터를 수정할 수 없음
- 함수의 const: 함수가 멤버 변수를 수정하지 않음
4. Setter 함수 (설정자)
void setId(int id) { Id = id; }- 멤버 변수의 값을 설정
- private 멤버에 안전하게 접근하는 방법
유효성 검사가 있는 Setter
void setCourse(const Course& course, int index) {
if(index >= 0 && index < 10) { // 유효성 검사
Courses[index] = course;
} else {
cout << "invalid course index." << endl;
}
}5. const 참조 매개변수
void assignCourseToStudent(Student& student, const Course& course)const Course& course의 의미
&(참조): 복사 없이 원본을 참조 → 효율적const: 함수 내에서 course를 수정할 수 없음 → 안전
Student& student (const 없음)
- 참조로 받되, 수정 가능
- 실제로 student를 수정하므로 const 없음
6. 배열 멤버 변수
private:
Course Courses[10]; // 고정 크기 배열- 각 학생은 최대 10개의 강의를 들을 수 있음
- 초기화: 기본 생성자로 자동 초기화됨
7. 포인터 반환
const Course* getCourses() const {return Courses; }- 배열 이름(
Courses)은 자동으로 포인터로 변환됨 const Course*: 반환된 포인터로 Course 객체를 수정할 수 없음
8. 범위 기반 for 루프 (Range-based for loop)
for(const auto& student : students) {
// student 사용
}auto: 타입 자동 추론 →Student로 추론됨const &: 복사 없이 참조하되, 수정하지 않음- 벡터의 모든 요소를 순회
9. 벡터 초기화
vector<Course> courses = {
Course(101, "Youngjun Won", 507),
Course(102, "KUTZNER", 202),
Course(103, "Yoon-ki Lee", 203)
};전체 흐름 요약
1. Course, Student 클래스 정의 (멤버 변수는 private, 함수는 public)
2. 생성자로 객체 초기화
3. Getter로 데이터 읽기 (const로 안전성 보장)
4. Setter로 데이터 쓰기 (유효성 검사 포함)
5. 참조와 const를 적절히 사용하여 효율성과 안전성 확보