S Single Responsibility Principle (단일 책임 원칙)
: 클래스는 하나의 책임만 가져야 한다
O Open/Closed Principle (개방-폐쇄 원칙)
: 확장에는 열려 있고, 변경에는 닫혀 있어야 한다
L Liskov Substitution Principle (리스코프 치환 원칙)
: 자식 클래스는 언제나 부모 클래스 대신 사용 가능해야 한다
I Interface Segregation Principle (인터페이스 분리 원칙)
: 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다
D Dependency Inversion Principle (의존 역전 원칙)
: 추상에 의존해야지, 구체에 의존하면 안 된다
클래스는 오직 하나의 변경 이유만 가져야 한다.
✳️ 의미
✅ 예
class ReportPrinter {
public:
void printReport();
};
class ReportSaver {
public:
void saveReport();
};
✔️ 출력과 저장을 각각 다른 클래스에 분리!
확장에 열려 있고, 변경에는 닫혀 있어야 한다.
✳️ 의미
✅ 예
class Shape {
public:
virtual void draw() = 0;
};
class Circle : public Shape {
void draw() override { std::cout << "Draw Circle\n"; }
};
class Renderer {
public:
void render(Shape* shape) { shape->draw(); }
};
✔️ 새로운 도형을 추가해도 Renderer는 수정할 필요 없음 → 확장에는 열려 있고, 기존에는 닫힘
자식 클래스는 부모 클래스로 대체할 수 있어야 한다.
✳️ 의미
❌ 나쁜 예
class Bird {
public:
virtual void fly();
};
class Ostrich : public Bird {
void fly() override {
throw "타조는 못 날아요!";
}
};
✔️ Ostrich는 Bird이지만 fly() 기능을 위반함 → LSP 위배
인터페이스는 작게 나누어야 하고, 클라이언트는 자신이 쓰지 않는 함수에 의존하면 안 된다.
✳️ 의미
❌ 나쁜 예
class IMachine {
public:
virtual void print() = 0;
virtual void scan() = 0;
virtual void fax() = 0;
};
class OldPrinter : public IMachine {
void print() override {}
void scan() override {} // ❌ 필요 없음
void fax() override {} // ❌ 필요 없음
};
✔️ 하나의 인터페이스에 너무 많은 기능을 담음 → 사용하지 않는 기능도 구현 강요
고수준 모듈은 저수준 모듈에 의존하면 안 된다. 둘 다 추상에 의존해야 한다.
✳️ 의미
✅ 예
class IMessage {
public:
virtual void send(const std::string& msg) = 0;
};
class Email : public IMessage {
void send(const std::string& msg) override {
std::cout << "Email: " << msg << '\n';
}
};
class Messenger {
IMessage* service;
public:
Messenger(IMessage* s) : service(s) {}
void deliver() {
service->send("Hello");
}
};
✔️ Messenger는 Email에 직접 의존하지 않고 IMessage라는 추상에만 의존
**템플릿(template)**은 자료형에 관계없이 일반화된 코드를 작성하기 위한 문법입니다.
✔️ 즉, 같은 로직을 여러 타입에 대해 반복적으로 작성하지 않고도
하나의 코드로 여러 자료형을 처리할 수 있습니다.
📌 기본 문법
template <typename T>
T add(T a, T b) {
return a + b;
}
📌 사용 예
std::cout << add<int>(3, 4); // 7
std::cout << add<double>(1.5, 2.3); // 3.8
또는 자동 타입 추론도 가능:
std::cout << add(3, 4); // T는 int로 추론
std::cout << add(1.5, 2.3); // T는 double로 추론
📌 기본 문법
template <typename T>
class Box {
private:
T value;
public:
Box(T v) : value(v) {}
T get() const { return value; }
};
📌 사용 예
Box<int> intBox(10);
Box<std::string> strBox("Hello");
std::cout << intBox.get(); // 10
std::cout << strBox.get(); // Hello
template <typename T1, typename T2>
class Pair {
public:
T1 first;
T2 second;
Pair(T1 a, T2 b) : first(a), second(b) {}
};
Pair<int, std::string> p(1, "hi");
template <typename T>
class Printer {
public:
void print(T value) {
std::cout << value << "\n";
}
};
// 문자열 특수화
template <>
class Printer<const char*> {
public:
void print(const char* value) {
std::cout << "문자열: " << value << "\n";
}
};
⚠️ 단점 및 주의점
std::string은 C++ 표준 라이브러리에서 제공하는 동적 문자열 클래스.
✔ 내부적으로 std::vector처럼 동적 메모리를 관리하며
✔ 길이, 복사, 비교, 연결, 검색, 수정 등 다양한 문자열 연산을 지원합니다.
✅ 기본 사용법
#include <iostream>
#include <string>
int main() {
std::string str = "Hello";
std::cout << str << "\n";
}
std::string s1; // 빈 문자열
std::string s2("hello"); // 문자열 리터럴로 초기화
std::string s3(5, 'A'); // "AAAAA"
std::string s4 = s2; // 복사 생성
append() 함수 통해 연결 가능✅ 문자열 반복
std::string s = "Hello";
for (char c : s)
std::cout << c << " ";
for (size_t i = 0; i < s.size(); ++i)
std::cout << s[i];
✅ 문자열 입력
std::string name;
std::cin >> name; // 공백 전까지 읽음
std::getline(std::cin, name); // 한 줄 전체 읽음
✅ C 스타일 문자열과 호환
const char* cstr = str.c_str(); // std::string → const char*
std::string str2 = std::string(cstr); // const char* → std::string
#include <iostream>
#include <string>
#include <filesystem>
#include <fstream>
using namespace std;
namespace fs = std::filesystem;
int main()
{
fs::create_directories("MyDirectory");
ofstream outFile("MyDirectory/myFile.txt");
outFile << "Hello, FileSystem Library!" << endl;
outFile.close();
cout << "Files in MyDirectory:\n";
for (const fs::directory_entry& entry : fs::directory_iterator("MyDirectory"))
{
if (entry.is_regular_file())
{
std::cout << entry.path().filename() << endl;
}
}
ifstream inFile("MyDirectory/myFile.txt");
string line;
while (getline(inFile, line))
{
cout << line << endl;
}
inFile.close();
// fs::remove_all("MyDirectory");
return 0;
}
✅ 1. #include , namespace fs = std::filesystem;
📌 설명
✅ 2. 디렉토리 생성
fs::create_directories("MyDirectory");
📌 설명
✅ 3. 파일 쓰기 – std::ofstream
ofstream outFile("MyDirectory/myFile.txt");
outFile << "Hello, FileSystem Library!" << endl;
outFile.close();
📌 설명
#include <fstream> 필요<< 연산자로 문자열을 쓸 수 있음.✅ 4. 디렉토리 내 파일 목록 출력
for (const fs::directory_entry& entry : fs::directory_iterator("MyDirectory"))
{
if (entry.is_regular_file())
{
std::cout << entry.path().filename() << endl;
}
}
📌 설명
fs::directory_iterator(path)로 지정 디렉토리 내의 모든 파일 및 서브디렉토리 탐색 가능.fs::directory_entry는 각각의 엔트리(파일/디렉토리/심볼릭링크 등)를 나타냄.entry.is_regular_file() → 일반 파일인지 확인entry.path().filename() → 파일명만 추출✅ 5. 파일 읽기 – std::ifstream
ifstream inFile("MyDirectory/myFile.txt");
string line;
while (getline(inFile, line))
{
cout << line << endl;
}
inFile.close();
📌 설명
if (!inFile.is_open()) {
cerr << "파일 열기에 실패했습니다!\n";
}
✅ 6. 파일/디렉토리 삭제
fs::remove_all("MyDirectory");
📌 설명
• 디렉토리 및 내부 모든 파일을 재귀적으로 삭제
• fs::remove_all(path)은 디렉토리 전체 삭제를 안전하게 수행
• fs::remove(path)은 파일이나 빈 디렉토리만 삭제 가능
혹은 바이너리 파일 입출력, 예외 처리 예시도 더 보여드릴게요! 😊