카테고리 없음

[C++] 기말고사 PPT정리

곽가누 2024. 6. 19. 21:56

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

  1. static 클래스 변수의 초기화:
    • 프로그램이 실행될 때, main 함수가 호출되기 전에 static 클래스 필드가 초기화됩니다.
    • 이는 클래스의 객체가 생성되기 전에 반드시 존재해야 하는 데이터를 의미합니다.
    • static 클래스 변수는 클래스의 인스턴스와 무관하게 존재합니다.
  2. static 메서드:
    • C++에서는 메서드를 static으로 선언할 수 있습니다.
    • static 메서드는 클래스의 인스턴스가 아닌, 클래스 자체에 대해 실행됩니다.
    • 따라서 static 메서드는 클래스의 인스턴스 변수(즉, non-static 필드)에 접근할 수 없습니다.
    • 또한, static 메서드는 다른 non-static 메서드를 호출할 수 없습니다.
    • static 메서드는 특정 클래스 인스턴스의 필드에 접근할 수 없기 때문에, 특정 인스턴스의 필드에 접근하기 위해서는 해당 인스턴스의 참조를 매개변수로 받아야 합니다.
  3. 제약 사항:
    • 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 다음의 주소를 나타냅니다.