내가 보려고 만든 C++ 기본 문법 정리

2023. 3. 3. 20:55Programming Languages/C++

컴파일러 담당 교수님께서 강의를 C++ 기반으로 진행한다고 하셔서 기본적인 C++ 문법을 정리한다. C++은 처음 써보는데 자바랑 C를 합쳐놓은 느낌이다.

기본

#include <iostream>                 // 헤더 포함할 땐 #include <헤더명>
using namespace std;                // 네임스페이스 적용할땐 using namespace 이름

void printName(string name);        // 함수 원형 선언

int main() {
    // 기본 자료형은 값이 할당되기 전에 쓰레기값이 들어있음
    int i = 4;                      // 정수형 변수. short 없음
    float f = 4.0;                  // 실수형 변수
    double d = 4.0;                 // 실수형 변수
    bool b = true;                  // 불린형 변수
    string greeting = "Hello";      // string 변수. char의 연속된 값
    string answer;
    char first = greeting[0];       // char 변수
    int* pi = &i;                   // 포인터 변수
    void (*fp)(string);             // 함수 포인터 변수
    
    fp = printName;                 // 함수 할당
    
    cout << greeting << "World!!";  // 출력할 땐 출력함수 << 값
    cin >> answer;                  // 입력할 땐 입력함수 >> 값
    cout << pi;                     // 0x7ffc8fccfcfc. i의 주소값
    cout << &pi;                    // 0x7ffd75375900. pi의 주소값
    
    fp("Jeidiiy");                  // Hello Jeidiiy
    
    return 0;
}

void printName(string name) {
  cout << "Hello " << name ;
}

조건문

#includue <iostream>
using namespace std;

int main() {
    int x = 1;
    int y = 2;
    
    // 기본 if 문
    if (x < y) {
        cout << y;
    } else if (x == y) {
        cout << "equal";
    } else {
        cout << x;
    }
    
    // 삼항 연산. 괄호 써야됨
    cout << (x < y) ? "X" : "Y";
    
    // switch 문
    switch (x) {
        case 1:
            cout << "X";
            break;
        case 2:
            cout << "Y";
            break;
        default:
            cout << "Z";
    }
}

반복문

#include <iostream>
using namespace std;

int main() {

    // while 문
    int i = 0;
    while (i < 5) {
        if (i == 4) continue;
        cout << i
        i++
    }
    
    // do-while 문
    int j = 0;
    do {
        cout << j;
        j++;
    } while (j < 5);
    
    // for 문
    for (int k = 0; k < 5; k++) {
        if (k == 4) break;
        cout << k;
    }
}

배열

#include <iostream>
using namespace std;

int main() {
    // 문자열 배열. 변수명 뒤에 배열 크기가 주어진다.
    string companies[4] = {"Apple", "Tesla", "Google", "Nvidia"};
    // 배열 크기 미지정
    string cars[] = {"Hyundai", "Kia", "BMW"};
    
    // for 문으로 순회
    for (int i = 0; i < sizeof(companies) / sizeOf(string); i++) {
        cout << companies[i] << "\n";
    }
    
    // foreach 문으로 순회
    for (string car : cars) {
        cout << car << "\n";
    }
}

구조체

#include <iostream>
using namespace std;

// 외부에 이름 있는 구조체 정의
struct Human {
  int age;
  string name;
  
  // 함수 정의 가능
  // 구조체의 함수 정의 시 하나의 함수를 모든 구조체에서 공유함
  int getAge() { return age; }
  string getName() { return name; }
  void setAge(int _age); // 함수 원형 정의
}

// 외부에서 함수 정의. 참조는 :: 기호 사용
void Human::setAge(int _age) {
  age = _age;
}

int main() {
  // 내부에도 구조체 정의 가능
  struct {
    string brand;
    string model;
    int year;
  } car1, car2;
  
  Human kim;
  Human park;
  
  kim.name = "Kim";
  kim.age = 26;
  
  park.name = "Park";
  park.age = 23;
  
  car1.brand = "Kia";
  car1.model = "K5";
  car1.year = 2010;
  
  // 이렇게도 초기화 가능
  car2 = {"Tesla", "Model X", 2015};

  cout << kim.name << ", " << kim.age;
  cout << park.name << ", " << park.age;
  cout << car1.brand << ", " << car1.model << ", " << car1.year;
}

클래스  정의

#include <iostream>
using namespace std;

class Car {
  // 모든 접근 범위에서 접근 가능
  public:
    string brand;
  
  // 클래스 내부 및 자식 클래스에서 접근 가능. 외부에서 접근 불가
  protected:
    string model;
    
  // 클래스 내부에서만 접근 가능. 자식 클래스나 외부에서 접근 불가
  private:
    int year;
}

// 접근 지정자 미지정시 기본적으로 private
class Human {
  int age;
  string name;
  
  // 생성자. 생성자 생략 시 기본 생성자만 생성됨
  public:
    // 생성자 위임
    Human(): Human(20, "John Doe") {
    }
    
    Human(int _age): Human(_age, "John Doe") {
    }
    
    // 생성자 선언
    Human(int _age, string _name);
    
    // 메서드 정의
    int getAge() { return age; }
    
    // 메서드 선언
    string getName();
}

// 생성자 외부 정의
Human::Human(int _age, string _name) {
  age = _age;
  name = _name;
}

// 메서드 외부 정의
Human::getName(string _name) {
  name = _name;
}

int main() {
  Human human1; // 기본 생성자로 생성
  Human human2(22);
  Human human3(24, "Spark");
  
  cout << human1.getName() << ", " << human1.getAge();
  cout << "\n";
  cout << human2.getName() << ", " << human2.getAge();
  cout << "\n";
  cout << human3.getName() << ", " << human3.getAge();
  
  return 0;
}

소멸자(Destructor)

소멸자는 객체가 소멸될 때 자동으로 실행되는 클래스의 멤버 함수다. 생성자는 클래스의 초기화를 돕도록 설계됐지만 소멸자는 청소를 돕도록 설계되었다.

지역에서 생성된 객체가 지역 범위를 벗어나거나 동적으로 할당된 객체가 삭제 키워드를 사용해 명시적으로 삭제되면, 객체가 메모리에서 제거되기 전에 필요한 정리를 수행하기 위해 클래스는 소멸자가 있는 경우 소멸자를 자동으로 호출한다.

클래스의 멤버 변수들이 단순하게 기본 자료형이 값 형식이라면 크게 필요 없지만 다른 리소스(예: 동적 메모리, 파일 또는 데이터베이스 핸들러)라면 객체가 소멸되기 전에 어떤 종류의 유지보수를 해야 한다.

소멸자 규칙

  • 소멸자 이름은 클래스 이름과 같아야 하고 prefix로 ~ 를 붙여야 한다.
  • 소멸자는 인수와 반환값이 없다.

이런 규칙 때문에 소멸자는 클래스당 하나만 존재한다. 또한 소멸자를 명시적으로 호출하는 일은 없다.

#include <iostream>
using namespace std;

class MyArray {
  private:
    int* myArray;
    int length;
    
  public:
    MyArray() {
      length = 10;
      myArray = new int[length];
      cout << "MyArray is ready" << "\n";
    }
    
    ~MyArray() {
      delete[] myArray; // 동적 배열 해제 시 괄호 꼭 붙이기!!
      cout << "MyArray is done";
    }
};

int main() {
  MyArray myArray; // 클래스가 생성될 때 생성자 호출
  
  cout << "Program is working..." << "\n";

  return 0; // 함수가 끝날 때 MyArray도 제거되므로 이때 MyArray의 소멸자 호출
}

클래스 상속

#include <iostream>
using namespace std;

// 기반 클래스
class Human {
  int age;
  string name;
  public:
    Human() {
      age = 20;
      name = "Robert Dauni JR";
    }
}

// 상속 클래스
class Hero: public Human {
  public:
    string skill;
    string nickname;
    
    Hero() {
      skill = "Suite";
      nickname = "Iron Man";
    }
}

int main() {
  Hero hero;
  
  // Human의 private 멤버인 age와 name은 접근 불가
  cout << hero.skill << ", " << hero.nickname;
}

인터페이스

C++은 언어 차원에서 인터페이스를 지원하지는 않는다. 대신 클래스의 prefix로 I를 붙여서 사용한다. 그리고 가상 함수를 만들어 구현 클래스가 이를 구현하도록 만든다. 이러한 가상 함수에는 몇 가지 규칙이 있다.

  • 클래스의 public 섹션에 선언한다.
  • 가상 함수는 static일 수 없으며 다른 클래스의 friend 함수가 될 수 없다
  • 가상 함수는 런타임에 다형성을 얻기 위해 포인터 또는 참조를 통해 접근해야 한다.
  • 가상 함수의 프로토타입(반환형과 매개변수)은 기반 클래스와 구현 클래스가 동일해야 한다.
  • 클래스는 가상 소멸자를 가질 수 있지만 가상 생성자를 가질 수 없다.
#include <iostream>
using namespace std;

// 인터페이스
class IParent {
  public:
    virtual void v_print() = 0; // 함수를 0(NULL)로 초기화. 구현하지 않은 상태로 클래스를 사용하면 에러나도록 유도
};

// 인터페이스를 구현한 구현 클래스
class Parent: public IParent {
  public:
    void v_print() {
      cout << "Parent" << "\n";
    }
    
    void print() {
      cout << "Parent" << "\n";
    }
};

// Parent를 상속한 클래스
class Child: public Parent {
  public:
    void v_print() {
      cout << "Child" << "\n";
    }
    
    void print() {
      cout << "Child" << "\n";
    }
};

int main() {
  // IParent ip; virtual 함수를 가진 클래스는 변수 선언조차 컴파일 오류를 발생시킨다.
  Parent* p;
  Child c;
  p = &c;
  
  p->v_print();  // Child
  p->print();    // Parent
  
  return 0;
}

Friend 키워드

하나의 클래스에서 다른 클래스의 내부 데이터에 접근해야 할 경우 friend 키워드를 사용해서 이용할 수 있다. 다만 이러면 캡슐화 파괴의 원인이 되므로 가급적 쓰지 말고 정말 필요한 경우에만 사용해야 한다.

Friend 함수

#include <iostream>
using namespace std;

class Person {
  private:
    string name;
  public:
    Person(string name) { this.name = _name; }
    
    friend string getName(Person p); // friend 함수
}

// friend 함수이므로 private 접근 지정자인 name에 접근 가능
string getName(Person p) {
  return p.name;
}

int main() {
  Person p("김씨");
  cout << p.getName(p); // 김씨

  return 0;
}

Friend 클래스

#include <iostream>
using namespace std;

class PersonA {
  private:
    string name;
    int age;
    friend class PersonB; // friend 클래스 선언
    
  public:
    PersonA(string name, int age) {
      this->name = name;
      this->age = age;
    }
};

class PersonB {
  public:
    void printInfo(PersonA pa) {
      cout << pa.name << ", " << pa.age;
    }
};

int main() {
  PersonA pa("김씨", 20);
  PersonB pb;
  
  pb.printInfo(pa); // 김씨, 20

  return 0;
}

점과 화살표 차이

  • 점(.)은 직접 참조
  • 화살표(->)는 포인터를 통한 간접 참조
#include <iostream>
using namespace std;

class Person {
  string name;
  
  public:
    Person() { name = "Kim"; }
    string getName() { return name; }
};

int main() {
  Person p;
  Person* pp = &p;
  
  cout << p.getName(); // 직접 참조
  cout << pp->getName(); // 간접 참조

  return 0;
}

참고사이트

소멸자, 동적 배열 할당 - https://boycoding.tistory.com/205