c++ 프로그래밍 (1) : c언어와의 차이점
#include <iostream>
int main(int argc, char** argv) {
std::cout << "HELLO WORLD" << std::endl;
return 0;
}
///////////////
using namespace std;
int main(int argc, char** argv) {
int a, b;
cin >> a >> b;
cout << a << "+" << b << "=" << a+b << endl;
return 0;
}
1. 헤더파일
c++에서 표준 헤더파일은 .h 를 안붙여줘도 된다. (커스텀은 .h 확장자 붙여줘야함)
2. 네임스페이스
std 는 네임스페이스. C++에서는 같은 이름을 가진 식별자가 있을 수 있는데, 네임스페이스를 명시하여 구분할 수 있다.
using namespace std; 를 통해 사용할 네임스페이스를 지정하면 주석 아래 코드처럼 std를 생략할 수 있다.
3. 입출력
cout은 함수가 아닌 객체이며 << 연산자를 이용하여 출력내용을 객체의 버퍼로 삽입한다.
c++은 c언어처럼 출력할때나 입력할때 %d, %s, %c, %f 등으로 타입을 지정해주지 않아도 된다.
4. 데이터타입
#include <iostream>
#include <string>
int main(){
std::string name;
std::cout << "이름 입력 : ";
std::cin >> name;
std::string message = "안녕하세요, " + name + " 님.";
std::cout << message << std::endl;
}
string 이라는 타입을 지원하며 배열 형태가 아니기때문에 길이를 지정하지 않아도된다.
c언어에서는 문자열이 배열로 만들어지며 strcat으로 합쳤지만, 문자열을 + 연산자로 합칠 수 있다.
5. 초기화
c++ 에서는 int a = 10; 과 int a; a = 10; 이 다르다. 또한 int a(10); 과 같은 모양으로도 사용할 수 있다. (왜다른지는 나중에)
6. 범위기반 for문
int main(){
int arr[10] = {3,1,4,1,5,9,2,6,5,3};
for(int i=0; i<10; i++){
cout << arr[i] << endl;
}
for(int n : arr){
cout << n << endl;
}
}
위의 for문과 아래의 for문이 동일한 역할을 수행하는데, n이라는 지역변수가 arr의 각 원소가 되어 출력하게된다.
7. 레퍼런스 변수, l-value, r-value
void swap (int& a, int& b){
int tmp = a;
a = b;
b = tmp;
}
int main(){
int a = 5;
int& p = a;
p = 10;
int arr[10] = { 3, 1, 4, 1, 5, 9, 2 , 6, 5, 3 };
for( int& n : arr ){ // n의 주소에 arr의 각 원소의 주소가 전달된다고 보면됨
n++ ; // == *n++
}
for( int n : arr ){ // arr의 각 원소의 값을 가져온다. (위에서 +1 씩 됨)
cout << n << endl;
}
}
포인터와 비슷한데, 변수에 별명을 붙여주는 변수라고 생각하면 쉽다. 포인터는 주소를 저장하는 변수를 새로 생성하고 그 주소에 다른 변수의 주소를 담는 방식이라면 레퍼런스변수는 변수를 가리킬수 있는 이름을 추가하는 것이다.
int& p = a; 에서 p라는 레퍼런스 변수를 선언하고 a의 주소를 가져왔기 때문에 p와 a는 공용체(union)와 같은 느낌으로 같은 메모리 공간에 존재한다.
C++11 에서 rvalue, lvalue 개념이 도입됐다.
C : lvalue는 대입 시 왼쪽 혹은 오른쪽에 오는 식이고, rvalue는 대입 시 오직 오른쪽에만 오는 식
C++ : lvaue는 어떤 메모리 위치를 가리키는데 &를 통해 그 위치를 참조할 수 있으며 rvalue는 lvalue가 아닌 모든값.
std::string a = "abc";
std::string b = a;
// 오른쪽에 있는 값들은 전부 rvalue이다.
std::int c = 2;
std::int d = c*2;
std::int e = c*d;
a="abc"; 코드에서 "abc"는 메모리공간(.rdata)에 저장된 뒤 a 라는 변수가 이 메모리공간을 가리키도록(move)한다.
b=a; 코드에서 a는 이미 메모리공간의 "abc" 라는 데이터를 가리키고있는데, b에 값을 넘겨주기 위해 a의 값 "abc"를 다른 메모리 공간(스택)에 복사(copy)하고 스택에 복사된 "abc"는 b가 가리키는 상태가 된다.
이렇게 a, b는 lvalue 라고 부르며, "abc" 같이 move를 통해 전달되는 값은 rvalue라고 한다.
#include <iostream>
#include <string>
using namespace std;
void storeByValue(string s) {
string b = s;
cout << "b1=" << &b << endl;
cout << "s1=" << &s << endl << endl;
}
void storeByLRef(string& s) {
string b = s;
cout << "b2=" << &b << endl;
cout << "s2=" << &s << endl << endl;
}
void storeByRRef(string&& s){
// string b = s;
string b = move(s);
cout << "b3=" << &b << endl;
cout << "s3=" << s << endl << endl;
}
int main() {
string a = "abc";
cout << " a=" << &a << endl;
cout << " t=" << &"test" << endl<<endl;
storeByValue(a);
storeByLRef(a);
storeByRRef("def");
}
레퍼런스 변수는 자신과 대입한 변수가 동일화 되는것이다.
1) storeByValue
a 가 string으로 선언되면서 스택에 메모리 공간이 생기고, .rdata영역에 "abc"가 생성되어 이 값을 가리키게 된다.
string s 를 파라미터로 전달받을 때, 스택에 s의 공간이 생기며 a의 값 "abc"가 .rdata에서 복사된 뒤 s가 가리키게된다.
b 값에 s를 저장할때에도 b가 스택에 공간이 할당되며, "abc"가 .rdata에 복사되고 그 복사된 영역을 b가 가리키게된다.
2) storeByLRef
&하나를 붙인 변수는 좌측값 참조 변수(lvalue reference)라고 한다.
string& s 레퍼런스 변수를 파라미터로 전달받는데, 이 변수는 전달받은 a와 동일하게 된다. &a == &b
b 변수는 스택에 생성되며 s의 값 "abc"가 복사되고 그 값을 가리키게 된다.
3) sotreByRRef
&두개를 붙인 변수는 우측값 참조 변수(rvalue reference)라고 한다.
우측값 참조 변수는 우측값(상수, 식 등)을 참조하는 변수이다. string&& s = "abc"; 라는건 "abc" 라는 .rdata에 생성되는 문자열상수에 s라는 별명을 붙였다고 보면 된다. 변수는 "abc"를 가리키는 s를 힙메모리공간에 할당한거라면, &&s는 "abc"의 별명그니까 "abc" 그 자체이다.
이 변수는 copy 최적화를 위해 move와 함께 사용하도록 만들어졌다.
move(s) 설명 나중에 다시공부
8. 오버로딩
void swap(int& a, int& b);
void swap(int*& a, int*& b);
void swap(double a, double b);
C언어에서는 컴파일러가 함수명을 보고 정의부를 찾기 때문에 동명함수가 존재하면 에러가 발생하지만, C++에서는 함수명과 매개변수를 한꺼번에 보기 때문에 동일한 이름이라도 매개변수가 다르면 컴파일이 가능하다.
9. 함수 기본값
int inventory[64] = { 0 };
int score = 0;
void setItem(int itemId, int num=5);
void getItem(int itemId, int cnt=1, int sc=0){
inventory[itemId] += cnt;
score += sc;
}
void setItem(int itemId, int num){
...
}
int main(){
getItem(3); // cnt, sc 변수는 기본값을 사용
getItem(6,3); // sc 변수는 기본값을 사용
getItem(11,34,7000);
}
파라미터에 디폴트 값을 정의해줄 수 있기 때문에 파라미터 일부를 넘기지 않아도된다. 하지만 디폴트 값을 지정한 파라미터는 오른쪽에 몰려있어야 한다.
함수 선언부에만 디폴트 값을 지정하면 된다.