자료형
char형
char a;
scanf("%c", &a);
printf("당신이 입력한 문자는 %c입니다.\n", a);
전치와 후치의 차이
b = a++; //후치의 경우 b에 a의 값을 할당한 후 a의 값을 1증가시킴
b = ++a; //전치의 경우 먼저 a의 값을 1증가시키고 b에 a의 값을 할당함
switch구문
int main(void) {
int choice;
printf("새 게임: 1\n");
printf("불러오기: 2\n");
printf("설정: 3\n");
printf("크레딧: 4\n");
printf("원하는 설정을 입력하세요.:");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("새 게임\n");
break;
case 2:
printf("불러오기\n");
break;
case 3:
printf("설정\n");
break;
case 4:
printf("크레딧\n");
break;
default:
printf("잘못 입력하셨습니다.\n");
break;
}
}
자연수의 약수를 구하는 문제
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void) {
int num;
printf("자연수를 입력하세요.:");
scanf("%d", &num);
if (num <= 0) {
printf("잘못 입력하셨습니다.\n");
}
else {
for (int i = 1; i <= num; i++) {
if (num % i == 0) {
printf("%d ", i);
}
}
}
printf("\n");
}
포인터 연산
배열 포인터
int main(void) {
int arr[3] = { 1, 2, 3 };
int (*ptr_arr)[3];//길이가 3인 int형 배열을 가리키는 포인터를 선언
ptr_arr = &arr;
for (int i = 0; i < 3; i++) {
printf("%d\n", (*ptr_arr)[i]);
}//1, 2, 3 출력
}
2차원 배열을 가리키는 포인터
int arr[2][3] = { {1, 2, 3}, {4, 5, 6} };
int(*ptr)[3];//2차원 배열을 가리킬 때는 배열 포인터와 가리킬 배열의 열 정보가 필요하다.
ptr = arr;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", ptr[i][j]);
}
printf("\n");
}
포인터 배열
int arr[4] = { 1, 2, 3, 4 };
int* ptr[4];
for (int i = 0; i < 4; i++) {
ptr[i] = &arr[i];
}
for (int i = 0; i < 4; i++) {
printf("%d ", *ptr[i]);
}
printf("\n");
배열 포인터와 포인터 배열의 선언 차이
문자열이 저장되어 있는 배열을 가리키는 포인터 배열
char arr[3][10] = { "Hello", "World", "Doodle" };
char* ptr_str[3];
for (int i = 0; i < 3; i++) {
ptr_str[i] = arr[i];
}
for (int i = 0; i < 3; i++) {
printf("%s\n", ptr_str[i]);
}
2차원 배열 + 포인터 예제
int arr[3][3] = {0};
printf("%d\n", &arr);//arr[0][0]의 주소를 출력
printf("%d\n", arr);//arr == &arr[0]이므로 arr[0][0] 주소를 출력
printf("%d\n", *arr);//arr이 가리키는 것은 첫번째 행 전체, 즉 일차원 배열 전체를 가리킴 이때 일차원 배열 전체를 출력하면 그 일차원 배열의 주소값이 출력됨
printf("%d\n", &arr[0]);//첫번째 행의 주소를 의미, 첫번째 행의 주소는 arr[0][0]의 주소와 같음
printf("%d\n", arr[0]);//arr[0] == &arr[0][0] 따라서 arr[0][0]의 주소를 출력
printf("%d\n", *arr[0]);//arr[0][0]의 값을 출력
printf("%d\n", &arr[0][0]);//arr[0][0]의 주소를 출력
printf("%d\n", arr[0][0]);//arr[0][0]의 값을 출력
//------------------------------------------------
int arr[3][3] = { 0 };
printf("%d\n", &arr[0][0]);//arr[0][0] 주소 출력
printf("%d\n", arr[0] + 1);//arr[0][0]의 주소 + 4 출력
printf("%d\n", &arr[0] + 1);//arr[1][0]의 주소 출력, arr[0][0] + 12 출력
printf("%d\n", arr + 1);//arr[1][0]의 주소 출력, arr[0][0] + 12 출력
printf("%d\n", &arr + 1);//arr[0][0] + 36 출력
c++ 스타일 입력, 출력
#include <iostream>
#include <string>
using namespace std;
int main(void) {
int a;
cin >> a;
cout << a << endl;
}
범위 기반 for문
#include <iostream>
#include <string>
using namespace std;
int main(void) {
int arr[] = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3 };
for (int n : arr) {//변수 n에 arr의 원소를 하나씩 대입하고 마지막 원소까지 대입이 끝나면 for문을 빠져나감
cout << n << endl;
}//주의할 점은 n이 reference 변수가 아니므로 실제 배열의 값을 변경하는 것은 불가능함
for (int& n : arr) {//n을 reference 변수로 선언함으로써 이제는 배열의 값을 변경하는 것이 가능함
n++;
cout << n << endl;
}
}
범위 기반 for문으로 이차원 배열 출력하기
int main(void) {
int arr[2][3] = { {1, 2, 3}, {4, 5, 6} };
for (int(&ln)[3] : arr) {
for (int& col : ln) {
cout << col << ' ';
}
cout << endl;
}
}
디폴트 매개변수
int item[64] = { 0 };
int score = 0;
void getItem(int itemId, int cnt = 1, int sc = 0) {//cnt 또는 sc의 값이 들어오지 않는다면 기본적으로 설정되어 있는 값을 변수에 할당하여 함수의 연산을 진행함
score += sc;
item[itemId] += cnt;
}
int main(void) {
getItem(6, 5);
getItem(3, 2);
getItem(3);
getItem(11, 34, 7000);
cout << score << endl;
for (int i = 0; i < 16; i++) {
cout << item[i] << ' ';
}
cout << endl;
}
namespace
int n;
void set() {
::n = 10;//::을 붙여줌으로써 n이 전역변수임을 표현할 수 있다.
}
namespace doodle {
int n;
void set() {
doodle::n = 20;//doodle에 있는 n에 20을 할당해라
}
}
namespace goodle {
int n;
void set() {
goodle::n = 30;
}
}
int main(void) {
::set();
doodle::set();
goodle::set();
cout << ::n << endl;
cout << doodle::n << endl;
cout << goodle::n << endl;
}
int n;
void set();
namespace doodle {
int n;
void set();
}
namespace goodle {
int n;
void set();
}
int main(void) {
::set();
doodle::set();
goodle::set();
cout << ::n << endl;
cout << doodle::n << endl;
cout << goodle::n << endl;
}
void ::set() {
::n = 10;//::을 붙여줌으로써 n이 전역변수임을 표현할 수 있다.
}
void doodle::set() {
doodle::n = 20;//doodle에 있는 n에 20을 할당해라
}
void goodle::set() {
goodle::n = 30;
}
namesapce 안의 namespace
int n;
void set() {
n = 10;
}
namespace doodle {
int n;
void set() {
n = 20;
}
namespace google {
int n;
void set() {
n = 30;
}
}
}
int main(void) {
::set();
doodle::set();
doodle::google::set();
cout << ::n << endl;
cout << doodle::n << endl;
cout << doodle::google::n << endl;
}
클래스
struct Tv {
bool powerOn;
int channel;
int volume;
void setVolume(int vol) {
if (vol >= 0 && vol <= 100) {
volume = vol;
}
}
};
int main(void) {
Tv lg;
lg.powerOn = true;
lg.channel = 10;
lg.setVolume(50);
}
접근 제어 지시자
struct Tv {
private:
bool powerOn;
int channel;
int volume;
public:
void on() {
powerOn = true;
cout << "TV를 켰습니다." << endl;
}
void off() {
powerOn = false;
cout << "TV를 껐습니다." << endl;
}
void volumeSet(int vol) {
if (vol >= 0 && vol <= 100) {
volume = vol;
}
}
void channelSet(int cnl) {
if (cnl >= 0 && cnl <= 999) {
channel = cnl;
}
}
};
int main(void) {
Tv lg;
lg.on();
lg.channelSet(10);
lg.volumeSet(50);
}
생성자를 호출하는 여러가지 방법
class Complex {
public:
Complex(double real_ = 0, double unreal_ = 0) {
real = real_;
unreal = unreal_;
}
double getReal() {
return real;
}
void setReal(double real_) {
real = real_;
}
double getUnreal() {
return unreal;
}
void setUnreal(double unreal_) {
unreal = unreal_;
}
private:
double real;
double unreal;
};
int main(void) {
Complex c1;
Complex c2 = Complex(2, 3);
Complex c3(2, 3);
Complex c4 = { 2, 3 };
Complex c5 = Complex{ 2, 3 };
Complex c6{ 2, 3 };
}
생성자 위임
class Time {
public:
Time() : h(0), m(0), s(0) {}
Time(int s_) : Time(){
s = s_;
}
Time(int m_, int s_) : Time(s_){
m = m_;
}
Time(int h_, int m_, int s_) : Time(m_, s_){
h = h_;
}
int h;
int m;
int s;
};
int main(void) {
Time t1;
Time t2(5);
Time t3(3, 16);
Time t4(2, 42, 15);
cout << "t1: " << t1.h << ":" << t1.m << ":" << t1.s << endl;
cout << "t2: " << t2.h << ":" << t2.m << ":" << t2.s << endl;
cout << "t3: " << t3.h << ":" << t3.m << ":" << t3.s << endl;
cout << "t4: " << t4.h << ":" << t4.m << ":" << t4.s << endl;
}
정적인 함수(static)를 사용하는 경우
메소드의 상수화
int ViewMoney() const {
return money;
}
매개변수의 상수화
void Deposit(const int d) {
money += d;
cout << d << "원을 예금했다." << endl;
}
연산자 오버로딩
class Vector2 {
public:
Vector2();
Vector2(float x, float y);
float GetX() const;
float GetY() const;
Vector2 operator+(const Vector2 rhs) const;
Vector2 operator-(const Vector2 rhs) const;
Vector2 operator*(const float rhs) const;
Vector2 operator/(const float rhs) const;
float operator*(const Vector2 rhs) const;
private:
float x;
float y;
};
Vector2 sum(Vector2 a, Vector2 b) {
return Vector2(a.GetX() + b.GetX(), a.GetY() + b.GetY());
}
int main(void) {
Vector2 a(2, 3);
Vector2 b(-1, 4);
Vector2 c1 = a - b;
Vector2 c2 = a * 1.6;
Vector2 c3 = a / 2;
float c4 = a * b;
cout << a.GetX() << ", " << a.GetY() << endl;
cout << b.GetX() << ", " << b.GetY() << endl;
cout << c1.GetX() << ", " << c1.GetY() << endl;
cout << c2.GetX() << ", " << c2.GetY() << endl;
cout << c3.GetX() << ", " << c3.GetY() << endl;
cout << c4 << endl;
}
Vector2::Vector2() : x(0), y(0) {}
Vector2::Vector2(float x, float y): x(x), y(y) {}
float Vector2::GetX() const { return x; }
float Vector2::GetY() const { return y; }
Vector2 Vector2::operator+ (const Vector2 rhs) const {
return Vector2(x + rhs.x, y + rhs.y);
}
Vector2 Vector2::operator- (const Vector2 rhs) const {
return Vector2(x - rhs.x, y - rhs.y);
}
Vector2 Vector2::operator* (const float rhs) const {
return Vector2(x * rhs, y * rhs);
}
Vector2 Vector2::operator/ (const float rhs) const {
return Vector2(x / rhs, y / rhs);
}
float Vector2::operator* (const Vector2 rhs) const {
return (x * rhs.x, y * rhs.y);
}
c++ 스타일 동적할당
int* num = new int(5);//new가 하는 일은 메모리 공간에 5를 저장할 공간을 만들어서 5를 저장하고 그 메모리 공간의 주소를 반환하는 것
int len;
int* arr;
cout << "배열의 크기를 입력하세요.";
cin >> len;
arr = new int[len];//c++에서는 malloc 대신 new를 사용하여 동적할당을 한다.
delete num;
delete[] arr;//우리가 할당한 공간은 반드시 해제해야 한다
얕은 복사, 깊은 복사
int* a = new int(5);
int* b = new int(3);
a = b;//얕은 복사
*a = *b;//깊은 복사
delete a;
delete b;
깊은 복사를 이해하기 위한 String 클래스 만들어 보기
class String {
public:
String() {
cout << "String() 생성자 호출" << endl;
strData = NULL;
len = 0;
}
String(const char* str) {
cout << "String(char* str) 생성자 호출" << endl;
len = strlen(str);
strData = new char[len + 1];
cout << "strData 할당: " << (void*)strData << endl;
strcpy(strData, str);
}
String(const String &rhs) {
cout << "String(String &str) 생성자 호출" << endl;
len = rhs.len;
strData = new char[len + 1];
cout << "strData 할당:" << (void*)strData << endl;
strcpy(strData, rhs.strData);
}
~String() {
cout << "~String() 소멸자 호출" << endl;
delete[] strData;
cout << "strData 해제됨" << endl;
strData = NULL;
}
String& operator=(String& rhs) {
if (this != &rhs) {
delete[] strData;
cout << "String(String &str) 생성자 호출" << endl;
len = rhs.len;
strData = new char[len + 1];
cout << "strData 할당:" << (void*)strData << endl;
strcpy(strData, rhs.strData);
return *this;//this는 그 객체의 주소를 가지고 있는 변수. 따라서 *this를 하게 되면 그 객체 자체를 의미한다.
}
}
char* GetstrData() {
return strData;
}
int Getlen() {
return len;
}
private:
char* strData;
int len;
};
int main(void) {
String s1("Hello world");
String s2(s1);
String s3("Hello");
s3.operator=(s1);
//s3가 operator= 함수를 호출했기 때문에 operator= 함수의 this에는 s3의 주소가 저장되어 있다. 그 후 return으로 s3 객체가 주어지지만 현재 코드에서 아무런 작업도 하지 않고 있으므로 무시가 된다.
cout << s1.GetstrData() << endl;
cout << s2.GetstrData() << endl;
cout << s3.GetstrData() << endl;
}
얕은 복사, 깊은 복사를 이해하기 위한 Polygon 클래스 만들어 보기
struct Point {
int x, y;
};
class Polygon {
public:
Polygon() {
nPoints = 0;
points = NULL;
}
Polygon(const int nPoints, const Point* points): nPoints(nPoints) {
this->points = new Point[nPoints];
for (int i = 0; i < nPoints; i++) {
this->points[i] = points[i];
}
}
Polygon(Polygon &x) {//복사 생성자
nPoints = x.nPoints;
points = new Point[nPoints];
for (int i = 0; i < nPoints; i++) {
points[i] = x.points[i];
}
cout << "깊은 복사" << endl;
}
Polygon(Polygon&& rhs) {//이동 생성자
nPoints = rhs.nPoints;
points = rhs.points;
rhs.points = NULL;
cout << "얕은 복사" << endl;
}
~Polygon() {
delete[] points;
}
Polygon& operator=(const Polygon& rhs) {//복사 대입 연산자
if (this != &rhs) {
delete[] points;
nPoints = rhs.nPoints;
points = new Point[nPoints];
for (int i = 0; i < nPoints; i++) {
points[i] = rhs.points[i];
}
}
cout << "깊은 복사" << endl;
return *this;
}
Polygon& operator=(Polygon&& rhs) {//이동 대입 연산자
if (this != &rhs) {
nPoints = rhs.nPoints;
delete[] points;
points = rhs.points;
rhs.points = NULL;
}
cout << "얕은 복사" << endl;
return *this;
}
int GetnPoints() const {
if (nPoints == 0) return NULL;
return nPoints;
}
Point* Getpoints() {
return points;
}
private:
int nPoints;
Point* points;
};
Polygon getSquare() {
Point points[4] = { {0, 0}, {0, 1}, {1, 0}, {1, 1} };
Polygon p(4, points);
return p;
}
int main(void) {
Polygon a;
a = getSquare();//얕은 객체 복사 2회
Polygon b = a;//깊은 객체 복사 1회
Polygon c;
c = a;//깊은 객체 복사 1회
int nPoints = c.GetnPoints();
Point* points = c.Getpoints();
for (int i = 0; i < nPoints; i++) {
cout << "(" << points[i].x << "," << points[i].y << ")" << endl;
}
//main 함수의 흐름: a = getSquare()가 실행이 되면 getSquare 함수에서 Point 배열을 메모리 공간에 생성함. 현재 p의 points가 Point 배열을 가리키고 있는 상태.
//이때 getSquare 함수가 return을 하게 되면 임시 객체가 생성이 되고 얕은 객체 복사가 한번 일어나게 됨. 임시 객체에 points2라는 변수가 있다고 가정하면 points2가 Point 배열을 가리키게 한다.
//성공적으로 복사가 이루어지면 getSquare 함수가 종료될 때 p의 소멸자가 Point 배열을 소멸시키는 것을 방지하기 위해서 p의 points와 Point 배열의 연결을 끊어버림
//그 이후에 임시 객체와 a 사이에서 위의 과정이 반복됨. 이 작업을 이동 대입 연산자를 사용하지 않고 진행하면 Point 배열이 3개가 생성되어 메모리 공간 낭비가 발생한다.
}
벡터
int main(void) {
vector<int> v1 = { 1, 2, 3, 4 };
v1.push_back(9);
v1.push_back(9);
v1.pop_back();
v1.shrink_to_fit();//vector의 늘어난 공간을 사이즈에 맞게 줄인다.
v1.insert(v1.begin(), 5);
v1.erase(v1.begin());
cout << v1.front() << endl;
cout << v1.back() << endl;
cout << v1.size() << endl;
cout << v1.capacity() << endl;//capacity는 vector의 현재 용량, size는 vector가 현재 가지고 있는 요소의 수
for (int& v : v1) {
cout << v << endl;
}
}
클래스 상속에서의 접근 제어자
간단한 클래스 상속해보기(메시지 관리 클래스)
class Image {
public:
operator string() {
return "사진";
}
};
class Message {
public:
Message(int sendtime, string name) : sendtime(sendtime), name(name) {}
int Getsendtime() const {
return sendtime;
}
string Getname() const {
return name;
}
private:
int sendtime;
string name;
};
class TextMessage: public Message {
public:
TextMessage(int sendtime, string name, string sendMessage)
: Message(sendtime, name), sendMessage(sendMessage) {}
string GetsendMessage() const {
return sendMessage;
}
private:
string sendMessage;
};
class ImageMessage: public Message {
public:
ImageMessage(int sendtime, string name, Image* image)
: Message(sendtime, name), image(image) {}
Image* GetImage() const {
return image;
}
private:
Image* image;
};
int main(void) {
Image* p_dogImage = new Image();
TextMessage* hello = new TextMessage(10, "doodle", "hello");
ImageMessage* dog = new ImageMessage(20, "doodle", p_dogImage);
cout << "보낸 시간: " << hello->Getsendtime() << endl;
cout << "보낸 사람: " << hello->Getname() << endl;
cout << "내용: " << hello->GetsendMessage() << endl;
cout << endl;
cout << "보낸 시간: " << dog->Getsendtime() << endl;
cout << "보낸 사람: " << dog->Getname() << endl;
cout << "내용: " << (string)*dog->GetImage() << endl;
cout << endl;
delete hello;
delete dog;
delete p_dogImage;
}
부모 클래스와 자식 클래스에 동일한 이름의 변수가 존재할 때 처리 방식
class Base {
public:
int a = 10;
};
class Derived : public Base {
public:
int a = 20;
};
int main(void) {
Base b;
Derived d;
cout << b.a << endl;//10 출력
cout << d.a << endl;//20 출력
cout << d.Base::a << endl;//10 출력
cout << d.Derived::a << endl;//20 출력
}
정적바인딩
class Base {
public:
int a = 10;
void print() {
cout << "From Base!!" << endl;
}
};
class Derived : public Base {
public:
int a = 20;
void print() {
cout << "From Derived" << endl;
}
};
class Derived2 : public Base {
};
int main(void) {
Base* b = new Derived();//Base 클래스가 Derived 클래스의 부모 클래스이므로 부모 클래스 타입의 포인터 변수가 자손 클래스를 참조하는 것이 가능
b->print();//b가 Derived를 참조하는 것은 맞지만 정적바인딩에 의해 b는 Base 타입의 변수이므로 Base에 존재하는 print 함수를 호출
b = new Derived2();
b->print();//Derived2 클래스에는 print 함수가 없음에도 코드에 에러가 나지 않는 이유는 정적바인딩 때문이다.
}
동적바인딩, 가상 함수(virtual)
class Weapon {
public:
Weapon(int power): power(power) {}
virtual void Use() {//가상 함수
//virtual을 붙여주면 정적바인딩이 아닌 동적바인딩이 가능해진다.
//정적바인딩에서와 달리 부모 클래스 타입의 포인터 변수가 자손 클래스를 참조하고 있는 경우에 자손 클래스에 Use 함수가 존재한다면 자손 클래스에 있는 Use 함수를 실행시킨다.
cout << "Weapon::Use()" << endl;
}
private:
int power;
};
class Sword : public Weapon {
public:
Sword(int power): Weapon(power) {}
void Use() {
cout << "Sword::Use()" << endl;
Swing();
}
private:
void Swing() {
cout << "Swing Sword." << endl;
}
};
class Magic : public Weapon {
public:
Magic(int power, int manaCost) : Weapon(power), manaCost(manaCost) {}
void Use() {
cout << "Magic::Use()" << endl;
Cast();
}
private:
int manaCost;
void Cast() {
cout << "Cast Magic." << endl;
}
};
int main(void) {
Sword mySword(10);
Magic myMagic(15, 7);
mySword.Use();
myMagic.Use();
Weapon* currentWeapon;
currentWeapon = &mySword;
currentWeapon->Use();//부모 클래스인 Weapon 클래스에서 Use 함수에 virtual이 붙어있으므로 자손 클래스에 Use 함수가 있는지 확인 후 있다면 자손클래스의 함수를 실행시킴
currentWeapon = &myMagic;
currentWeapon->Use();
}
동적바인딩의 편리함
class Image {
public:
operator string() {
return "사진";
}
};
class Message {
public:
Message(int sendtime, string name) : sendtime(sendtime), name(name) {}
int Getsendtime() const {
return sendtime;
}
string Getname() const {
return name;
}
virtual string GetContent() const {
return "";
}
private:
int sendtime;
string name;
};
class TextMessage: public Message {
public:
TextMessage(int sendtime, string name, string sendMessage)
: Message(sendtime, name), sendMessage(sendMessage) {}
string GetsendMessage() const {
return sendMessage;
}
string GetContent() const {
return sendMessage;
}
private:
string sendMessage;
};
class ImageMessage: public Message {
public:
ImageMessage(int sendtime, string name, Image* image)
: Message(sendtime, name), image(image) {}
Image* GetImage() const {
return image;
}
string GetContent() const {
return (string)*image;
}
private:
Image* image;
};
void printMessage(Message* m) {
cout << "보낸 시간: " << m->Getsendtime() << endl;
cout << "보낸 사람: " << m->Getname() << endl;
cout << "내용: " << m->GetContent() << endl;
cout << endl;
}
int main(void) {
Image* p_dogImage = new Image();
Message* messages[] = {
new TextMessage(10, "doodle", "hello"),
new TextMessage(11, "doodle", "hello"),
new TextMessage(12, "doodle", "hello"),
new ImageMessage(20, "doodle", p_dogImage)
};
for (Message* m : messages) {
printMessage(m);
}
delete p_dogImage;
}
순수 가상 함수, 추상 클래스
class Shape {//추상 클래스
public:
virtual double getArea() = 0;//순수 가상 함수
virtual void Resize(int x) = 0;//순수 가상 함수
};
class Rectangle : public Shape {
public:
Rectangle(double a, double b): a(a), b(b) {}
double getArea() {
return a * b;
}
void Resize(int x) {
a *= x;
b *= x;
}
private:
double a, b;
};
class Circle : public Shape {
public:
Circle(double r) : r(r) {}
double getArea() {
return PI * r * r;
}
void Resize(int x) {
r *= x;
}
private:
double r;
};
int main(void) {
Shape* shapes[] = {
new Rectangle(20, 30),
new Circle(10)
};
for (Shape* s : shapes) {
s->Resize(2);
}
for (Shape* s : shapes) {
cout << s->getArea() << endl;
}
}
인터페이스
#define interface struct