
특강 듣고 도전실습 코드 돌렸더니 에러가 136개가 나왔다ㄷㄷ
개선!



Aircraft클래스에서 Pilot crew[2]로 정의되어있는데 만약에 입력이 덜들어 올것을 대비해서 Pilot에 기본 생성자 만들어줌
#ifndef STUDENT_H_
#define STUDENT_H_
// 헤더 파일 내용
class Student {
// ...
};
#endif
Include Guards방식에서는 #ifndef, #endif를 활용해서 정의되어있는 헤더를 다시 컴파일하지 않게 막았음
장점
단점
그래서 등장한게 #pragma once이다.
#pragma once
// 헤더파일 내용
...
장점
단점
특징
장점
유지보수 용이코드 재사용성 향상궁금한거
int a = 10;
a = 20;
-> 대입과 정의를 비교해야 한다.
메모리 입장에서는
int a = 10; // 정의 메모리주소 0x1000에 공간생성 10저장
a = 20; // 같은 메모리주소 0x1000의 값만 변경
// 정의가 한번 만 가능하다는 것은
int a = 30; // 이렇게 a를 또 쓰면 에러
객체가 소멸될때 자동으로 호출되는 특별한 멤버 함수
~ 붙임
정리작업 수행
생성자 반대 역할
매개변수, 반환값 없음(void도 안붙임)
public선언
객체 자신의 주소를 가리키는 포인터
모든 비정적 멤버 함수에 자동으로 전달
타입 : 클래스명* const (포인터 상수)
특징
객체 멤버 변수를 변경하지 않겠다고 보장하는 함수
특징
class Student {
public:
void study() { // non-const
cout << "공부 중\n";
}
void showInfo() const { // const
cout << "정보 출력\n";
}
};
const Student s;
s.study(); // ❌ 에러! const 객체는 non-const 함수 호출 불가
s.showInfo(); // ✅ OK! const 함수는 호출 가능
그래서 왜씀?
포인터는 메모리를 저장하는 변수
* : 포인터 선언 및 역참조& : 주소 가져오기레퍼런스는 기존 변수의 별명(alias)
const int* p1
int a = 10;
const int* p1 = &a;
*p1 = 20; // ❌ 에러! 가리키는 값 변경 불가
p1 = &b; // ✅ OK! 포인터 자체는 변경 가능
int* const p2
int a = 10, b = 20;
int* const p2 = &a;
*p2 = 30; // ✅ OK! 가리키는 값 변경 가능
p2 = &b; // ❌ 에러! 포인터 자체 변경 불가
const int* const p3
int a = 10, b = 20;
const int* const p3 = &a;
*p3 = 30; // ❌ 에러! 가리키는 값 변경 불가
p3 = &b; // ❌ 에러! 포인터 자체 변경 불가
int a = 10;
int* p = &a;
int** pp = &p;
cout << a; // 10
cout << *p; // 10
cout << **pp; // 10
사용사례
포인터는 값(주소)을 저장하는 변수이므로, 함수에서 포인터를 변경하려면 포인터의 포인터가 필요하다.
void allocateMemory(int** pp){
*pp = new int(100); // 포인터가 가리키는 곳 변경
}
int main(){
int* ptr = nullptr;
allocateMemory(&ptr); // ptr자체를 변경
cout << *ptr; // 100
delete ptr;
}
void allocateMemory(int* p){
p = new int(100);
}
int main(){
int* ptr = nullptr;
allocateMemory(ptr); // ptr은 여전히 nullptr(복사본만 변경됨)
}
int** matrix = new int*[rows]; // 행 배열
for (int i = 0; i < rows; i++){
matrix[i] = new int[cols];
}
matrix[0][0] = 10;
for (int i = 0; i < rows; i++){
delete[] matrix[i];
}
delete[] matrix;
void printMatrix(int** matrix, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << matrix[i][j] << " ";
}
cout << "\n";
}
}
struct Node {
int data;
Node* next;
};
void deleteNode(Node** head, int value) {
if (*head == nullptr) return;
if ((*head)->data == value) {
Node* temp = *head;
*head = (*head)->next; // head 포인터 자체를 변경!
delete temp;
return;
}
}
문자열을 숫자로 바꾸는것에 대해서 많이 생각하게 되는 문제
stoi의 경우 숫자가 아니면 error를 발생해서 try/catch문으로 받아야하는데 이거는 알고리즘에서는 크게 의미 없는거 같고
isdigit은 0~9까지의 일의자리 숫자만 가능하다.
그래서 현실적으로는 문자열을 하나씩 뜯어봐서 isdigit을 각각 쓰는게 나아보인다.
sort(vec.begin(), vec.end(), [](const string& a, const string& b)
{
if (a.length() != b.length())
{
return a.length() < b.length();
}
return a < b;
});
기존에는 이렇게 사용했다.
bool compare(string& a, string& b)
{
if (a.length() != b.length())
{
return a.length() < b.length();
}
return a < b;
}
sort(vec.begin(), vec.end(), compare);
vec.erase(unique(vec.begin(), vec.end()), vec.end());
sort(vec.begin(), vec.end(), compare);
sort(vec.begin(), vec.end(), compare);
vec.erase(unique(vec.begin(), vec.end()), vec.end());
sort하고 unique + erase로 변경
그래도 이 방식이 입력받을때 find(vec.begin(), vec.end(), inp) == vec.end() 이 조건으로 계속 체크하는것보다는 효율적이다.
CPU 아키텍처는 CPU가 한번에 처리할 수 있는 데이터 크기
내부 레지스터 크기
OS비트수는 운영체제의 메모리 관리
프로그램에게 제공하는 주소공간 크기
CPU가 더크면 OS에서 돌아가지만 OS의 비트수가 더 크면 못돌림
생성자나 변환 연산자 앞에 붙여서 컴파일러의 암묵적(묵시적)형변환을 방지하는 키워드
explicit = 명시적인
장점
단점
사용할때