C++은 많이들 배우는 언어이다. 나도 이번 학기 동안 C++과 객체지향에 대해 배웠고 지금 그것을 정리하고자 한다. 내가 나중에 봤을 때 어떤 내용을 했었는지 기억에 남았으면 좋겠고, 내 머리에도 그것이 남았으면 좋겠다.
우선 c++은 c언어를 기반으로 제작된 것이 이름에서 부터 느껴진다. 이러한 c++을 통해서 우리는 객체지향에 대해 이해할 것이다. 프로그래밍은 현재 객체지향을 많이 사용하기 때문이다.
우선 간단한 내용부터 살펴볼 것인데 그것은 바로 변수이다.
수학에서 변수는 변하는 값이다. 2차원에서 변수는 보통 x, y 로 잡고 이것은 변하는 수로 이것을 통해 차원을 정의한다.
프로그래밍에서도 이러한 변수가 존재한다.
변수를 선언할 때는 변수의 타입과 이름을 정해주어야 한다.
변수의 타입이 뭔지 물어볼 수 있다. 변수의 타입은 정수, 실수, 문자, 문장 등으로 구분된다.
이를 컴퓨터 (컴파일러)가 알아듣기 쉽게 만들면, int, float, char, string 이다.
때문에 int IntVar;와 같은 형식으로 프로그래밍을 해주면 변수의 선언이 된다.
변수는 변하는 값이기에 변수에 값을 저장해줄 수 있다.
IntVar = 10; 과 같은 형식으로 값을 넣어줄 수 있다. 수학과 다르게 = 는 대입 연산자이다.
정수형에 실수형을 넣으면 어떻게 되나요?
예를 드렁서 IntVar = 10.99; 를 하면 어떻게 될까? 에러가 날까?
이는 컴파일러가 알아서 처리해 준다. 소수점을 버리는 형식으로 말이다.
쉽게 생각해서 10.99를 정수형으로 형 변환을 시켜서 저장해 준 것이다.
17.0 / 5의 값은 무엇일까요?
이 경우에는 3.4가 된다. 왜냐고 물을 수 있다. 뒤에 것이 정수 아닌가? 이러한 경우에 실수의 힘이 더 세기 때문에 5가 실수가 되면서 실수 연산이 된다.
이러한 것을 형 변환이라고 하는데 위의 경우에는 자동으로 형 변환이 된 것이다.
이러한 형 변환을 인위적으로 해줄 수 있는데 이는 static_cast <변수형> 변수 이름 과 같은 형식으로 해준다.
10이나 10.99는 변수가 아니다. 이들은 변하지 않는 값이다. 10는 10이고 9는 9이다. 때문에 이런 변하지 않는 값들을 우리는 상수라고 부른다.
상수는 만들 수도 있다. 3.141592 와 같은 값은 변하지 않는 값이다. 이러한 값을 우리는 상수로 만들어 줄 수 있는데 const double PI = 3.141592와 같은 선언을 해주면 된다.
일반적인 변수를 만드는 방법 앞에 const를 붙이기만 하면 된다.
float나 double을 그냥 출력하게 되면 뒤에 많은 소수점이 따라오는데 이를 없애는 방법이 있다.
cout.percision(2);를 해주면 소수점 2자리만 출력되는데 이는 반올림 된 값으로 나온다.
cout.setf(ios::fixed);를 해주면 위에서 선언한 자리수만큼 고정이 된다.
cout.setf(ios::showpoint);를 해주면 소수점을 표시해주게 된다.
int main () {
double a = 30;
double b = 10000.0;
double pi = 3.1416;
std::cout.precision (5);
std::cout << std::showpoint << a << '\t' << b << '\t' << pi << '\n';
std::cout << std::noshowpoint << a << '\t' << b << '\t' << pi << '\n';
return 0;
}
30.000 10000. 3.1416
30 10000 3.1416
우리는 변수를 시시각각 내 마음에 드는 것으로 넣어주고 싶을 때가 있다.
cin >> var1 >> var2;와 같은 형식을 사용한다.
이때 띄어쓰기를 통해서 변수를 분리해 줄 수 있다. 아니면 엔터를 써도 된다. 이러한 whitespace를 통해서 변수를 나누어 받는다.
swhitch (garade)
{
case "a":
case "A":
cout << "you are A" << endl;
break;
case "b":
case "B":
cout << "you are B" << endl;
break;
default:
cout << "???" << endl;
}
이런식으로 사용하면 원하는 위치로 점프할 수 있다.
if (n1 > n2)
max = n1;
else
max = n2;
이러한 선언 대신에 한줄로 간단하게 사용할 수 있는 것이 있다.
max = (n1 > n2) ? n1 : n2;
다음과 같은 식으로 깔끔한 코딩을 할 수 있다.
난수를 만드는 것은 사실 쉬운 일은 아니다. 패턴이 생길 수 있기 때문이다. 때문에 이러한 난수를 만드는 함수가 이미 구현되어 있다.
#include <time>
srand(time(0));
을 하면 난수를 생성할 수 있다.
#include <cassert>
이것을 사용하면 오류는 편하게 잡을 수 있다. 하지만 은근 고급기술이라 사실 초보자가 사용할 일은 많이 없을 수도 있다. 나도 초보자라 거의 써본 적이 없는데 사용은 간단하다.
내가 체크하고 싶은 부분에서, 원하는 결과가 맞는지 체크하기 위해서 사용한다.
assert(var > 10);
이런식으로 써두면 컴파일러가 맞으면 넘어가고 잘못되었으면 오류 창을 띄어주면서 디버깅하기 쉽게 만든다.
#define NDEBUG
#include <cassert>
를 해주게 되면 assert를 전체 다 끌 수 있다.
#include <fstream>
fstream inputStream;
inputStream.open("filename.txt");
inputStream >> var;
inputStream.close();
이제 기본적으로 알아야 하는 내용들이다. 이제부터 본격적으로 객체라는 것이 무엇인지에 대해서 생각을 해볼 것이다. 객체는 추상화를 기반으로 한 개념이다. 객체가 있고 그 객체는 이름과 기능이 있다. 예를 들어 자동차를 생각해보자. 자동차라는 객체는 이동한다는 기능이 있다. 뭐 부수적으로 엉덩이 온열 시트도 있을 것이다.
이러한 자동차는 종류가 다양하다. 아우디도 있고 현대도 있다. 자동차라는 큰 객체에서 보자면 이 둘의 기능은 동일하다.
내가 지금부터 할 내용들은 이러한 객체를 어떻게 만들고 사용하는지 살펴볼 것이다.
배열은 객체는 아니지만 필요한 내용이라서 써 놓을 것이다.
학생들이 시험을 보면 점수를 받아 올 것이다. 그럼 학생들 마다 시험 점수를 저장해야하는데 어떻게 저장할 것인가?
배열이 없다면 김일등점수 = 99; 강꼴등점수 = 4; 전중간_점수 = 64; 이런식으로 될 것이다. 하지만 귀찮다. 너무 귀찮다. 때문에 배열을 사용하는 것이다.
int student_scores [] = {99, 4, 64};
끝이다. 이렇게 해주면 된다. 이렇게 해서 안에 적절한 인덱스만 넣어주면 되는 것이다.
함수에 넣을 때는 어떻게 할까?
함수에 넣을 때는 배열의 이름만 보내거나 특정 주소를 보내주면 된다. 하지만 이 때 배열의 크기 정보를 보내주어야한다는 것이 특징이다.
void fillup(int student_scores [], int size);
or
void fillup(int* student_scores, int size);
사용하는 방법은 다음과 같다.
fillup(student_scores, int size);
배열 이름을 대상으로 산술연산도 가능하다. 이 경우에는 배열의 다음 인덱스를 가리키거나 하는 형식으로 진행된다. 배열끼리의 더하기는 오류를 만들어내니 주의하자.
2차원 배열도 만들어낼 수 있다.
int array[2][2] = {
{1,1,1},
{2,2,2},
{3,3,3}
};
이런식으로 만들어 주면 된다.
배열과 단짝 격인 친구가 포인터이다. 주소라는 개념이 들어가기 때문에 처음 공부하는 입장에서는 난감할 수 있다.
간단하게 생각해서 주소지를 들고 찾아 가는 것이다.
int var = 10;
int *p;
p = &var;
이렇게 쓰는데 p는 var를 참조하고 있는 것이다.
포인터에 변수를 할당해 주는게 아니라 새로운 공간을 만들어서 할당해 줄 수도 있는데 그것이 new 이다.
int * n ;
n = new int(17);
과 같은 형식으로 사용한다.
지울 때는
delete p;
p = NULL;
로 지운다.
이걸로 배열도 할당해 줄 수 있다.
typedef double* DoublePtr;
DoublePtr d;
d = new double[10];
과 같은 형식으로 사용한다.
지울 때는
delete [] d;
좀 특이하니 조심하자.
참고
int* someFunc(); (O)
int [] someFunc(); (X)
MyClass *p;
p = new MyClass;
p->grade = "A"; // == (*p).grade = "A";
month = mn; // These three statements
this->month = mn; // are equivalent
(*this).month = mn;
자기 자신을 가리키는 포인터가 내장되어 있어 이를 사용할 상황들이 있나보다.
그냥 정보를 담는 그릇이라고 생각하면 된다.
struct CDAccountV
{
double balance;
double balance;
int term;
};
class Basic
{
public:
// 생성자
// copy 연산자
// 산술 연산자
// 소멸자
private:
// 멤버 변수
};
class 새
{
'''
};
class 닭 : public 새
{
'''
};
생성자 소멸자는 상속되지 않고 나머지는 상속됨.
다시 정의 해서 덮을 수 있음.
// 기본
시급노동자 :: 시급노동자() : 노동자(), 시급(0), 노동시간(0)
{
// 비었음
}
// 인자 주기
시급노동자 :: 시급노동자(string 이름,
string 번호, double 시급, double, 시간) : 노동자(이름, 번호), 시급(시급), 노동시간(시간)
{
// 비었음
}
자식& 자식:: 연산자 = (const 자식& 오른쪽)
{
부모::연산자 = (오른쪽);
'''
}
class 새
{
'''
virtual 울기()
{
cout << "찌르르르" << endl;
}
'''
};
class 닭 : public 새
{
'''
virtual 울기 ()
{
cout << "꼬꼬 댁" << endl;
}
'''
};
virtual void draw() = 0 ;
함수에 0을 넣는게 조금 이상해 보이지만 이렇게 선언한다고 한다.
그리고 이런 가상함수가 있는 것을 추상 클래스라고 한다.
template<typename T>
Pair<T> :: Pair(T val1, T val2)
{
first = val1;
second = val2;
}
template<typename T>
void Pair<T> :: setFirst(T newVal)
{
first = newVal;
}
Pair<int> score;
Pair<char> seats;
score.setFirst(3);
탬플릿도 상속이 될까?
template <typename T>
class Triple : public Pair<T>
{
'''
}
#include <vector>
vector<int> cotainer;
cotainer.push_back(i);
vector<int>:: iterator p;
for (p=cotainer.begin(); p!= cotainer.end(); p++)
cout << *p " ";
#include <set>
set<char> s;
s.insert('A');
s.insert('D');
s.insert('D');
s.insert('C');
s.insert('C');
s.insert('B');
set<char> :: const_iterator p;
for (p=s.begin(); p!=s.end(); p++)
cout << *p << " " ;
=> A B C D
알아서 정렬되고 알아서 중복 제거 해 줌
map <string, string> planets;
planets["수"] = "쥰나 뜨겁다";
planets["금"] = "대기가 산성";
planets["지"] = "우리 별";
=> 금, 수, 지 (한글 생각한 순서)
얘도 알아서 정렬 됨.
std::sort (myvector.begin(), myvector.end());
std::sort (myvector.begin(), myvector.end(), myfunction);
//myfuction으로 역순으로 정렬도 가능;
where = find(mychar.begin((), mychar.end(), 'value');