#include <iostream>
using namespace std;
void process(int a){
cout << "Int 버전 호출됨" << endl;
}
void process(char a){
cout << "Char 버전 호출됨" << endl;
}
int main(){
short s = 50;
process(s);
return 0;
}
short 타입은 int, char중 어디로 변환될까? C++은 더 안전하다고 판단되는 변환을 선택한다.
현재 process 함수는 char와 int형으로 오버로딩 되어있다. main함수에서 short타입변수 s를 넣어주었는데 C++컴파일러는 short타입을 어떤 process함수에 전달할지 결정해야 한다.
변환 규칙은 여러가지가 있는데 어쨌던 short를 int로 변환하는것은 데이터 손실이 없지만 char형으로 변환하는것은 데이터 손실이 있을수있기 때문에 int형으로 변환한다.
설계
인덱스 연산자를 operator로 오버로딩해야함, 읽기/쓰기가 가능하려면 참조(T&)를 반환해야 한다. val=arr[0], arr[0] = val 둘다가 가능해야 한다.
예외처리의 경우에는 out_of_range를 사용해서 인덱스가 0보다 작거나 size보다 클때를 방어
#include <iostream>
#include <stdexcept>
using namespace std;
template <typename T>
class Array {
T data[100];
int size;
public:
Array() : size(0) {}
T& operator [](int index) {
if (index < 0 || index >= size) {
throw out_of_range("Index out of bounds");
}
return data[index];
}
void add(const T& element) {
if (size < 100)
data[size++] = element;
}
void remove() {
if (size > 0)
size--;
}
void print() {
for (int i = 0; i < size; i++)
cout << data[i] << " ";
cout << endl;
}
};
int main() {
try {
Array<int> arr; // 정수형 배열 생성
arr.add(10);
arr.add(20);
arr.add(30);
// 초기상태
arr.print();
// 값 읽기
cout << arr[0] << endl;
// 값 쓰기
arr[0] = 99;
cout << arr[0] << endl;
arr.print();
// 예외 처리
cout << arr[3] << endl;
} catch (const out_of_range& e) {
cerr << "예외 발생! 오류 메시지: " << e.what() << endl;
}
return 0;
}
// Range-based for loop 라는 문법이에요.
// 보통 foreach라고도 불려요.
for (int num : arr)
{
}
C++컴파일러는 for(auto e : arr)같은 for-each문을 만나면 내부적으로는
{
auto iter = arr.begin();
auto end_iter = arr.end();
for (;iter!=end_iter; ++iter){
auto e = *iter;
// 사용자 코드 실행
}
}
이런식으로 동작한다. 따라서 클래스안에 begin()과 end()가 존재해야 foreach문법이 작동한다.
왜 포인터(T*)를 반환할까?
Array클래스는 T data[100]이라는 기본 배열을 사용한다. C++에서 배열의 포인터는 그 자체로 훌륭한 이터레이터 처럼 동작한다. begin(), end()로 주소값만 던져주면 for문이 알아서 주소를 하나씩 증가시키며 데이터에 접근하게 된다.
...
T* begin() {
return &data[0];
}
T* end() {
return &data[size];
}
int main() {
Array<int> arr; // 정수형 배열 생성
arr.add(10);
arr.add(20);
arr.add(30);
for (int ar : arr) {
cout << ar << endl;
}
return 0;
}
#include <iostream>
using namespace std;
template<typename T>
class Array
{
T data[100];
int size;
public:
inline Array()
: size{0}
{
}
inline void add(const T & element)
{
if(this->size < 100) {
this->data[this->size++] = element;
}
}
inline T * begin()
{
return operator&(this->data[0]);
}
inline T * end()
{
return operator&(this->data[this->size]);
}
inline void remove()
{
if(this->size > 0) {
this->size--;
}
}
inline void print()
{
for(int i = 0; i < this->size; i++) {
operator<<(operator<<(std::cout, this->data[i]), " ");
}
std::cout.operator<<(std::endl);
}
};
/* First instantiated from: insights.cpp:38 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class Array<int>
{
int data[100];
int size;
public:
inline Array()
: size{0}
{
}
inline void add(const int & element)
{
if(this->size < 100) {
this->data[this->size++] = element;
}
}
inline int * begin()
{
return &this->data[0];
}
inline int * end()
{
return &this->data[this->size];
}
inline void remove();
inline void print();
};
#endif
int main()
{
Array<int> arr = Array<int>();
arr.add(10);
arr.add(20);
arr.add(30);
{
Array<int> & __range1 = arr;
int * __begin1 = __range1.begin();
int * __end1 = __range1.end();
for(; __begin1 != __end1; ++__begin1) {
int ar = *__begin1;
std::cout.operator<<(ar).operator<<(std::endl);
}
}
return 0;
}
vector<int> vec = {10, 20, 30, 40, 50};
auto it = vec.begin();
it = it + 3; // +, - 연산 가능
it += 2; // +=, -= 연산 가능
cout << it[2]; // [] 연산자로 접근 가능
cout << (it < vec.end());// 크기 비교 가능
vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it){
if (*it == 3){
vec.erase(it); // erase후 it는 무효화
// ++it하면 크래시 발생
}
}
// 올바른 코드
for (auto it = vec.begin(); it != vec.end(); ){
if (*it == 3){
it = vec.erase(it); // erase는 다음 유효한 반복자를 반환한다.
} else {
++it;
}
}
왜 무효화 될까?
vector는 내부적으로 연속적인 메모리 공간을 사용한다. 요소를 삽입하거나 삭제하면 메모리가 재배치될 수 있고, 이 과정에서 기존 반복자가 가리키던 주소가 더 이상 유효하지 않게 된다.
decltype = declared type
선언된 타입이 무엇인지 알려주는 역할을 한다.
int x = 10;
decltype(x) y = 20; // y는 int타입 선언
const int& ref = x;
decltype(ref) ref2 = y; // ref2는 const int& 타입으로 선언됨
....
int x = 5;
int& ref = x;
auto a = ref; // int (참조 제거됨)
decltype(ref) b = x; // int& 참조 유지됨