Class란 무엇일까?
Class는 사용자 지정 type이다.
Class는 오브젝트의 인스턴스니 뭐니 하는데 다 저 말로 종결됨.
fstream myFile;
fstream이 Class 가 되고 myFile은 인스턴스(오브젝트가 된다)
1. vector 에 클래스 지정하기
std::vector<Account> accounts(5000);
2. 함수 포인터와의 활용
#include <iostream>
#include <string>
#include <vector>
#include <algorithm> // for std::swap
using namespace std;
class Account {
public:
string name;
int id;
double balance;
Account(string n, int i, double b) : name(n), id(i), balance(b) {}
};
bool less_than_by_name(const Account& e1, const Account& e2) {
return e1.name < e2.name;
}
bool less_than_by_id(const Account& e1, const Account& e2) {
return e1.id < e2.id;
}
bool less_than_by_balance(const Account& e1, const Account& e2) {
return e1.balance < e2.balance;
}
void sort(std::vector<Account>& db,
bool (*comp)(const Account&, const Account&)) {
int size = db.size();
for (int i = 0; i < size - 1; i++) {
int smallest = i;
for (int j = i + 1; j < size; j++) {
if (comp(db[j], db[smallest]))
smallest = j;
}
if (smallest != i)
std::swap(db[i], db[smallest]);
}
}
int main() {
std::vector<Account> customers = {
Account("Alice", 1001, 5000.0),
Account("Bob", 1002, 1500.0),
Account("Charlie", 1003, 3000.0)
};
sort(customers, less_than_by_balance);
for (const Account& account : customers) {
cout << "Name: " << account.name
<< ", ID: " << account.id
<< ", Balance: " << account.balance << endl;
}
return 0;
}
3.
캡슐화(Encapsulation)란 무엇인가?
캡슐화(Encapsulation)는 객체 지향 프로그래밍(Object-Oriented Programming, OOP)의 핵심 개념 중 하나로, 데이터를 보호하고, 데이터에 대한 접근을 제한하는 기법입니다. 이를 통해 객체의 내부 상태를 외부에서 직접 접근하지 못하도록 하고, 객체 내부의 데이터와 메서드를 묶어서 관리할 수 있습니다.
4. const
const SimpleRational fract(1, 2); // 상수 객체 1/2
std::cout << fract.get_numerator(); // 오류 발생
const가 그저 get 하는거여도 무조건 안된다고 하기 때문.
따라서 const로 선언한 객체면 그에 대한 함수에도 const를 붙여줘야 접근 가능
class SimpleRational {
public:
int get_numerator() const {
return numerator;
}
int get_denominator() const {
return denominator;
}
private:
int numerator;
int denominator;
};
const 정리하자면
int const * const & function (int const * const & obj) const;
1)리턴타입에 위치한 const
반환값을 const 화 한다.
2)인자(아규먼트, 파라메터)에 위치한 const
parameter을 const화 한다.
어떤 함수에 인자를 넘겨주긴 해야겟는데
혹시 그것들이 함수 내부에서 변경될까 두렵다면 이 위치에 const를 남발하면 된다.
그렇게 하면 인자들은 내부에서 쓰이긴 해도 변경되지는 않는다.
3) 꼬리에 달린 const
어떤 클레스 내부의 메소드(멤버함수)가 클레스의 멤버필드(멤버변수)를 건들지 않는다고 명시해 줄때 쓴다.
출처 :
https://blog.naver.com/ak74r/90929179
Point(int x, int y);
void setPoint(int x, int y);
int getX(void)const;
int getY(void)const;
Point operator+( Point& point);
Point& operator=( const Point& point);
const를 그럼 필수적으로 써야하는 곳은 어디냐?
1. getter function 뒤에
2. 값이 붕 뜰때 const로 묶어놔야함
이런식으로 계산하고싶으면
*pP3 = *pP1 + (*pP2 * *pP2);
+ operator와 = operator는 const 써야함 2번처럼
5. 포인터 다른 표현
#include <iostream>
using namespace std;
class Point {
public:
double x;
double y;
};
int main()
{
Point pt1;
Point* pt2;
pt2 = &pt1;
pt1.x = 100.0;
pt1.y = 200.0;
cout << (*pt2).x << ":" << (*pt2).y;
cout << pt2->x << ":" << pt2->y;
}
두 cout은 같은 값이 나온다.
6. 포인터와 동적 할당
#include <iostream>
using namespace std;
class Point {
public:
Point(double _x, double _y) : x(_x), y(_y){}
double x;
double y;
};
int main()
{
Point* pt2;
pt2 = new Point(1.0, 2.0);
pt2->x = 100.0;
pt2->y = 200.0;
cout << (*pt2).x << ":" << (*pt2).y;
cout << pt2->x << ":" << pt2->y;
delete pt2;
}
이러면 pt1 없이도 코딩할 수 있다.
7. operator overloading
#include <iostream>
using namespace std;
class Point {
double x;
double y;
public:
Point();
Point(int x, int y);
void setPoint(int x, int y);
double getX(void) const;
double getY(void) const;
Point operator+(const Point& point) const;
Point& operator=(const Point& point);
};
Point::Point() {
x = y = 0;
}
Point::Point(int x, int y) {
this->x = x;
this->y = y;
}
void Point::setPoint(int x, int y) {
this->x = x;
this->y = y;
}
double Point::getX(void) const {
return this->x;
}
double Point::getY(void) const {
return this->y;
}
Point Point::operator+(const Point& point) const {
Point result(this->x + point.getX(), this->y + point.getY());
return result;
}
Point& Point::operator=(const Point& point) {
if (this != &point) { // 자기 할당 검사
this->x = point.getX();
this->y = point.getY();
}
return *this;
}
ostream& operator<<(ostream& os, const Point& point) {
return os << "(" << point.getX() << "," << point.getY() << ")";
}
int main() {
Point* pP1, * pP2, *pP3;
pP1 = new Point;
pP2 = new Point(1, 2);
pP3 = new Point; // pP3를 동적 할당하여 초기화
pP1->setPoint(10, 20);
*pP3 = *pP1 + *pP2;
cout << "[X:" << pP1->getX() << "][Y:" << pP1->getY() << "]" << endl;
cout << *pP3 << endl;
delete pP1;
delete pP2;
delete pP3; // 메모리 해제
return 0;
}
7-1. + operator
Point Point::operator+(const Point& point) const {
Point result(this->x + point.getX(), this->y + point.getY());
return result;
}
*pP3 = *pP1 + *pP2;
8. static
- static 클래스 변수의 초기화:
- 프로그램이 실행될 때, main 함수가 호출되기 전에 static 클래스 필드가 초기화됩니다.
- 이는 클래스의 객체가 생성되기 전에 반드시 존재해야 하는 데이터를 의미합니다.
- static 클래스 변수는 클래스의 인스턴스와 무관하게 존재합니다.
- static 메서드:
- C++에서는 메서드를 static으로 선언할 수 있습니다.
- static 메서드는 클래스의 인스턴스가 아닌, 클래스 자체에 대해 실행됩니다.
- 따라서 static 메서드는 클래스의 인스턴스 변수(즉, non-static 필드)에 접근할 수 없습니다.
- 또한, static 메서드는 다른 non-static 메서드를 호출할 수 없습니다.
- static 메서드는 특정 클래스 인스턴스의 필드에 접근할 수 없기 때문에, 특정 인스턴스의 필드에 접근하기 위해서는 해당 인스턴스의 참조를 매개변수로 받아야 합니다.
- 제약 사항:
- non-static 메서드는 호출된 객체의 인스턴스 변수에 접근할 수 있지만, static 메서드는 객체의 인스턴스 변수에 접근할 수 없습니다.
- 모든 클래스 메서드(static 또는 non-static)는 static 데이터 멤버나 다른 static 메서드에 접근할 수 있습니다.
- non-static 메서드는 암시적으로 this 포인터를 사용하여 호출된 객체를 참조할 수 있지만, static 메서드는 this 포인터가 없으므로, 이를 사용할 수 없습니다.
9. 선언만 하면 constructor가 실행되지 않음.
int main() {
Point* pP1, * pP2, * pP3;
constructor 실행 X
10. friend
friend가 붙은 function이나 class들은 private attribute들에도 접근 가능하다
#include <iostream>
using namespace std;
class Point {
double x;
double y;
static int countCreatedObjects;
public:
Point();
Point(int x, int y);
void setPoint(int x, int y);
int getX(void) const;
int getY(void) const;
static int getCreatedObject(void);
Point operator+(const Point& point);
Point& operator=(const Point& point);
friend std::ostream& operator<<(std::ostream& os, const Point& point);
friend class SpyPoint;
};
다음과 같이 friend 선언을 하면
<< 오버로딩
std::ostream& operator<<(std::ostream& os, const Point& point)
{
return os << "(" << point.x << "," << point.y
<< ")";
}
Spypoint
class SpyPoint {
public:
void printPoint(const Point& point);
};
void SpyPoint::printPoint(const Point& point)
{
cout << "{X:" << point.x << "}" << "{Y:" << point.y << "}" << endl;
}
메인함수
int main()
{
Point *pP1, *pP2;
SpyPoint SP;
cout << "Number of created object is : " << Point::getCreatedObject() << endl;
pP1 = new Point;
pP2 = new Point( 1, 2 );
pP1->setPoint( 10, 20 );
*pP2 = *pP1 + *pP2;
cout << "[X:" << pP1->getX() << "]" << "[Y:" << pP1->getY() << "]" << endl;
cout << *pP2 << endl;
cout << "Number of created object is : " << Point::getCreatedObject() << endl;
SP.printPoint(*pP1);
SP.printPoint(*pP2);
delete pP1;
delete pP2
;
}
getX 요지랄 안해도 바로 쓸 수 있음
11. Inheritance
부모껄 좀 응용해서 다른 걸 만들 수 있음(복사 후 변경됨)
자식 클래스는 부모 클래스의 인스턴스처럼 취급
12. inheritance type
#include <iostream>
using namespace std;
class Base {
public:
void f(void);
};
void Base::f(void) {
cout << "in function 'Base::f()'\n";
}
class Derived : private Base {
public:
void g(void);
using Base::f;
};
void Derived::g(void) {
cout << "in function 'Derived::g()'\n";
}
int main() {
Base myB;
Derived myD;
myB.f();
myD.g();
myD.f(); //error!!
return 0;
}
private로 상속받아서 에러남. public으로 상속받으면 에러 안남
상속받는 type은 부모 attribute의 최댓값을 정해줌
ex. protected로 상속받으면 public attribute도 protectd attribute로 상속됨
#include <iostream>
using namespace std;
class Base {
public:
void f(void);
};
void Base::f(void) {
cout << "in function 'Base::f()'\n";
}
class Derived : private Base {
public:
void g(void);
};
void Derived::g(void) {
Base::f();
cout << "in function 'Derived::g()'\n";
}
int main() {
Base myB;
Derived myD;
myB.f();
myD.g();
return 0;
}
그래서 private가 되면 부모 함수 직접 불러서 써야함
13. overloading
함수 오버로딩은 매개변수의 타입과 개수에 따라 구분된다.
14. 자식class를 부모 class에게 할당하면 부모 class에 해당하는 부분만 복사된다.
int main() {
Text t1("plain");
FancyText t2("fancy", "<<", ">>", "::");
std::cout << t1.get() << " " << t2.get() << ‘\n’;
t1 = t2;
// copy Derived class object to Base class object
std::cout << t1.get() << " " << t2.get() << '\n';
그리고 t2= t1은 안됨. 자식이 부모한테만 할당할 수 있다.
15. 다형성
int main() {
std::vector<Text *> texts {
new Text("Wow"),
new FancyText("Wee", "[", "]", "-"),
new FixedText,
new FancyText("Whoa", ":", ":", ":")
};
for (auto t : texts)
std::cout << t->get() << '\n';
for (auto t : texts) {
t->append("A");
t->append("B");
t->append("C");
}
for (auto t : texts)
std::cout << t->get() << '\n';
for (auto t : texts)
delete t;
return 0;
}
하나의 벡터 안에 여러개의 클래스들을 삽입할 수 있다.
16. virtual double span() const = 0;
지금 당장은 정의할 게 없고 상속받을 때 정의될 것
17. non-type parameter
#include <iostream>
template <int N>
int scale(int value) {
return value * N;
}
int main() {
std::cout << scale<3>(5) << '\n';
std::cout << scale<4>(10) << '\n';
}
#include <iostream>
template <typename T, int N>
T scale(const T& value) {
return value * N;
}
int main() {
std::cout << scale<double, 3>(5.3) << '\n';
std::cout << scale<int, 4>(10) << '\n';
}
return type을 정할 수 있다. double과 int가 각각 그 리턴 type
18. iterator 다양한 표현
#1. 포인터 사용
#include <iostream>
#include <vector>
// Print the contents of vector v, traversing with a
// caller supplied increment value (inc)
void print(const std::vector<int>& v, int inc) {
for (auto p = std::begin(v); p != std::end(v); p += inc)
std::cout << *p << ' ';
std::cout << '\n';
}
// Print the contents of the vector v backwards,
// traversing with a caller supplied decrement value (dec)
void print_reverse(const std::vector<int>& v, int dec) {
auto p = std::end(v);
while (p != std::begin(v)) {
p -= dec;
std::cout << *p << ' ';
}
std::cout << '\n';
}
int main() {
std::vector<int> vec(20);
for (int i = 0; i < 20; i++)
vec[i] = i;
print(vec, 1);
print(vec, 2);
print(vec, 4);
print(vec, 5);
print(vec, 10);
std::cout << '\n';
print_reverse(vec, 1);
print_reverse(vec, 2);
print_reverse(vec, 4);
print_reverse(vec, 5);
print_reverse(vec, 10);
}
#2. using Iter (Iterator) 사용
#include <iostream>
#include <vector>
using Iter = std::vector<int>::iterator;
// Count the elements in a vector of integers that match seek
int count_value(Iter iter_begin, Iter iter_end, int seek) {
int cnt = 0;
for (Iter cursor = iter_begin; cursor != iter_end; cursor++)
if (*cursor == seek)
cnt++;
return cnt;
}
int main() {
std::vector<int> a {
34, 5, 12, 5, 8, 5, 11, 2
}
;
// Count multiple elements
std::cout << count_value(std::begin(a), std::end(a), 5) << '\n';
// Count single element
std::cout << count_value(std::begin(a), std::end(a), 12) << '\n';
// Count missing element
std::cout << count_value(std::begin(a), std::end(a), 13) << '\n';
a = {
}
;
// Try an empty vector
std::cout << count_value(std::begin(a), std::end(a), 5) << '\n';
}
#3. template 사용
#include <iostream>
#include <vector>
#include <array>
#include <list>
#include <string>
// Count the elements in a range that match seek.
// Type Iter is an iterator type working with a container that
// contains elements of type T. Type T elements must be
// comparable with operator==.
template <typename Iter, typename T>
int count_value(Iter iter_begin, Iter iter_end, const T& seek) {
int cnt = 0;
for (auto cursor = iter_begin; cursor != iter_end; cursor++)
if (*cursor == seek)
cnt++;
return cnt;
}
int main() {
// Test with a vector of integers
std::cout << "---Vector of integers--------------\n";
std::vector<int> a {
34, 5, 12, 5, 8, 5, 11, 2
}
;
std::cout << count_value(std::begin(a), std::end(a), 5) << '\n';
a = {
}
;
// Try an empty vector
std::cout << count_value(std::begin(a), std::end(a), 5) << '\n';
std::cout << count_value(std::begin(a), std::end(a), 8) << '\n';
}
19. lambda 함수
#1. 다양한 표현
#include <iostream>
using namespace std;
// 함수 포인터를 받아 평가하는 함수
int evaluate(int (*f)(int, int), int x, int y) {
return f(x, y);
}
int main() {
int val;
// 유형 1: 일반적인 람다 함수 사용
val = evaluate([](int x, int y) -> int { return x * y; }, 2, 3);
cout << val << endl; // 출력: 6
// 유형 2: 간단하게 표현한 람다 함수 사용
val = evaluate([](int x, int y) { return x * y; }, 2, 3);
cout << val << endl; // 출력: 6
// 유형 3: 직접 람다 함수 호출
[](int x, int y) { std::cout << x << " " << y << '\n'; } (10, 20);
// 출력: 10 20
// 유형 4: 람다 함수를 변수에 할당하고 호출
auto f = [](int x) { return 5 * x; };
std::cout << f(10) << '\n'; // 출력: 50
return 0;
}
#2. function 사용
#include <iostream>
#include <functional>
// std::function을 사용하여 람다 함수를 인수로 받아 처리하는 함수
int evaluate2(std::function<int(int, int)> f, int x, int y) {
return f(x, y);
}
int main() {
int a;
std::cout << "Enter an integer: ";
std::cin >> a;
// 람다 함수 내에서 변수 a를 캡처하고 x와 y를 처리
std::cout << evaluate2([a](int x, int y) {
if (x == a)
x = 0;
else
y++;
return x + y;
}, 2, 3) << '\n';
return 0;
}
#3. function 사용 - 함수 이름부터 지정
#include <iostream>
#include <functional>
// 로컬 변수를 캡처하여 더하는 람다 함수를 반환하는 함수
std::function<int(int)> make_adder() {
int loc_val = 2; // 로컬 변수 정의
return [loc_val](int x) { // 로컬 변수를 캡처하는 람다 함수
return x + loc_val;
}; // 람다 함수를 반환
}
int main() {
auto f = make_adder(); // 람다 함수를 반환받아 f에 저장
std::cout << f(10) << '\n'; // f를 호출하여 결과 출력 (10 + 2 = 12)
std::cout << f(2) << '\n'; // f를 호출하여 결과 출력 (2 + 2 = 4)
return 0;
}
! algorithm library 필요 !
20. for_each 함수
#include <iostream>
#include <vector>
#include <algorithm> // for_each
void print(int x) {
std::cout << x << " ";
}
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 함수 포인터를 사용한 for_each
std::for_each(vec.begin(), vec.end(), print);
std::cout << std::endl;
// 람다 함수를 사용한 for_each
std::for_each(vec.begin(), vec.end(), [](int x) {
std::cout << x << " ";
});
std::cout << std::endl;
return 0;
}
21. copy 함수
int main() {
// 정수 벡터 생성
std::vector<int> seq {2, 3, 4, 5, 6};
// 벡터 크기가 2 이상일 경우에만 실행
if (seq.size() >= 2) {
// 크기가 seq.size() - 2인 벡터 생성
std::vector<int> seq2(seq.size() - 2);
// 첫 번째와 마지막 요소를 제외하고 seq의 요소들을 seq2로 복사
std::copy(std::begin(seq) + 1, std::end(seq) - 1, std::begin(seq2));
}
}
22. transform 함수
#include <iostream>
#include <string>
#include <algorithm> // std::transform
#include <cctype> // std::toupper
int main() {
std::string name = "Fred";
std::string str = "abcDEF-GHIjkl345qw";
// 대문자 변환 전 출력
std::cout << "Before: " << name << " " << str << '\n';
// name 문자열을 대문자로 변환
std::transform(std::begin(name), std::end(name), std::begin(name), ::toupper);
// str 문자열을 대문자로 변환
std::transform(std::begin(str), std::end(str), std::begin(str), ::toupper);
// 대문자 변환 후 출력
std::cout << "After : " << name << " " << str << '\n';
return 0;
}
- std::begin(name): name 문자열의 시작 반복자.
- std::end(name): name 문자열의 끝 반복자.
- std::begin(name): 변환된 문자를 저장할 name 문자열의 시작 반복자.
- ::toupper: 단항 함수로, 문자를 대문자로 변환하는 함수입니다.
23. 배열 memory
배열의 메모리 구조
- 배열 arr은 메모리에서 연속된 공간을 차지합니다.
- 예를 들어, 배열 arr가 5개의 정수로 구성되어 있고, 각각의 정수가 4바이트를 차지한다고 가정하면 배열 arr는 20바이트(5 * 4 바이트) 크기의 메모리를 차지합니다.
&arr과 &arr + 1의 의미
- &arr은 배열 자체의 주소를 가리킵니다. 이것은 배열 arr의 첫 번째 요소의 주소와 동일합니다.
- &arr + 1은 배열 arr 다음에 오는 주소를 가리킵니다. 이는 배열 arr의 크기(20바이트) 만큼 더해진 주소를 의미합니다.
*(&arr + 1)의 의미
- *(&arr + 1)은 &arr + 1이 가리키는 주소를 역참조합니다. 이는 배열 arr 다음의 주소를 나타냅니다.