C++

역사

1979년, 덴마크의 컴퓨터 과학자 비야네 스트롭스트룹은 C++의 선구자격 언어인 "C with Classes" 작업에 착수하였다. 새로운 언어를 만들려는 의의는 박사 논문을 위한 스트롭스트룹의 프로그래밍 경험에서 비롯되었다.
처음에 스트롭스트룹의 "C with Classes"는 C 컴파일러(Cpre)에 클래스, 상속 클래스, 스트롱 타이핑, 인라인 확장, 기본 인수를 포함한 여러 기능들을 추가하였다.
1983년, "C with Classes"라는 이름은 가상 함수, 함수 이름, 연산자 오버로딩, 참조, 제약조건, type-safe free-store 메모리 할당 (new/delete), 개선된 자료형 검사, BCPL 스타일의 1줄 코멘트(//)를 포함한 새로운 기능들을 추가하면서 "C++"로 변경되었다.
1985년 C++ 프로그래밍 언어 제1판이 출시되었으며 당시 공식 표준이 없었기 때문에 이 언어의 절대적인 참조 문헌이 되었다. 최초의 C++ 상용 구현체는 같은 해 10월에 출시되었다.

Hello world 프로그램

C++ 표준 라이브러리 스트림 기능을 사용하여 표준 출력을 통해 Hello, world!를 출력한다.
 1 # include <iostream> 2  3 using namespace std; 4  5 int main() { 6     
 7     cout << "Hello, world!" << endl; 8     
 9     return 0;10 }
가급적이면 void main을 사용하지 말자.  컴퓨터는 프로그램이 0이 아닌 수를 반환하면
그 프로그램이 제대로 종료되었다 생각하지 않는다. 즉 int main으로 숫자 0을 리턴하여
잘 종료되었다는 걸 컴퓨터에게 알려주자.

객체의 생성과 소멸

객체를 구현하기 위해 클래스의 선언이 필요하다. 구조적인 설정을 하고 생성하면 된다.

객체의 생성

C언어에서는 전역변수와 지역변수 그리고 동적할당에 의한 데이터 저장공간을 만들 수 있다. 마찬가지로 C++에서도 전역변수로 객체를 생성할수도 있고 지역변수로 생성하는것도 가능하다. 또한 new를 통해 동적으로 객체를 생성 시킬 수 있다.

정적 할당 객체

C++에서 변수를 잡듯이 객체도 선언을 통해 이루어진다. C에서 전역변수와 지역변수로 나누어는 것과 같이, 객체 역시 같은 방식으로 전역 또는 지역 객체로 선언할 수 있다.
전역 정적 객체는 main() 함수가 실행 되기 전 저장공간이 생성되고, 생성자가 호출된다. 객체가 생성되면 메모리에 데이터 저장공간이 생기고 바로 생성자가 호출된다. 따라서 전역 정적 객체 생성자의 호출은 main함수보다 먼저 이루어진다.

동적 할당 객체

new에 의해 생성 되고, delete에 의해 삭제 된다. new로 생성 되면, new 실행 시점에서 객체의 저장공간인 메모리를 확보하고 생성자가 자동실행 된다. 동적객체는 힙영역에 존재하고 포인터를 넘겨 받아서 객체 포인터 변수에 주소값을 저장하고 처리한다.
'new'의 코딩은 함수에 넣을 수 밖에 없으므로 main 함수 시작 이후에야 가능하다.

이미 설정된 메모리 공간을 이용하여 객체 정의하기

C/C++은 포인터를 사용한다. 이것은 객체의 크기만 확보되면 객체를 사용하는데 아무 문제가 없다. 따라서 이미 설정된 변수를 포인터를 이용하여 객체화 해서 사용할 수 있다.
#include <iostream>typedef struct MsgBuff {
    MsgBuff *link;
    int  szpkg;
    int  szdata;
    void *data;} MsgBuff;class A {public:
    A() { num = 0; }
    int num;};char gbuff[1024];int main(int argc, char* argv[]){
    MsgBuff *pmsg = (MsgBuff*) gbuff;
    int szpkg = sizeof(A)+sizeof(pmsg->link)+sizeof(int)*2;
    pmsg->link = NULL;
    pmsg->szdata = sizeof(A);
    pmsg->szpkg = szpkg;

    A *pa = (A*) &pmsg->data;
    pa->num = 10;
    std::cout << pa->num << std::endl;

    return 0;}

블럭에서의 정적 클래스 생성과 소멸

#include <iostream>using namespace std;class A {public:
   A() { num = 0; }
   A(int n) : num(n) { cout << "생성자 호출 : " << this << endl;  }
   ~A() { cout<< "소멸자 호출 : " << this << endl; }
   int getNum() { return num; }private:
   int num;};int main(){
   int num;

   cout << "블럭 시작 합니다." << endl;
   {  // 블럭 시작하면 지금 부터 선언되는 변수나 클래스는 블럭이 끝나면 사라진다.
      A a(10);

      num = a.getNum();
      cout << "블럭 안에서 a.getNum() = " << a.getNum() << endl;

   } // 블럭이 끝나면 객체 a는 소멸됨. 소멸자가 호출된다.
   cout << "블럭 끝났습니다. num = " << num << endl;
   return 0;}
실행결과:
블럭 시작 합니다.
생성자 호출 : 0039FCD0
블럭 안에서 a.getNum() = 10
소멸자 호출 : 0039FCD0
블럭 끝났습니다. num = 10
모든 함수는 블럭을 포함하므로 함수내에서 생성된 정적객체는 함수의 블럭이 끝나면 소멸자가 호출되고 객체의 저장공간은 사라진다. 위의 예처럼 임의의 블럭에서 생성된 정적객체 역시 자기의 블럭이 끝나면 소멸자가 호출되고 사라진다.

객체의 예

/// 파일 : MyObject.h ////////////////////////////////////class MyObject { // 클래스의 시작public:
   MyObject();   // 생성자1
   MyObject(int age, char *name);  // 생성자2 : 생성자는 여러가지가 가능하다.
   virtual ~MyObject();  // 소멸자

   // 멤버변수 age, name, fdyn가 privated이므로 다른 객체에서 접근이 불가능 하다.
   // 따라서 멤버함수로 해결한다. setAge, getAge,setName,getName

   void setAge(int age) { this->age = age; }  // 함수 본체를 클래스 내에 코딩할 수 있다.
                    // 모든 함수를 헤더의 클래스 안에 코딩하면 클래스 전체를 파악하는데 불편할 수 있다.
                    // 그리고 파일을 cpp와 h로 나누는 의미가 훼손될 수 있다.
   int getAge() { return age; }

   void setName(char *n, int opt = 0);  // 만약 opt을 사용하지 호출하면 자동으로 0을 설정한다.
   char* getName();

   static int counter;private:
   int age;
   char *name;
   int  fdynm;  // name 변수의 메모리 공간을 new (malloc())로 만들었는지를 설정};/// 파일 : MyObject.cpp //////////////////////////////////#include <iostream>#include "MyObject.h"using namespace std;MyObject::MyObject() // 생성자1{
   age = 0;
   name = NULL;
   fdynm = 0;

   cout << "+생성자1 MyObject() : " << this << endl;}MyObject::MyObject(int age, char *name) // 생성자2{
   this->age = age;
   this->name = name;

   cout << "+생성자2 MyObject("<< age
 <<","<< name << ") : " << this << endl;}MyObject::~MyObject() // 소멸자{
cout << "-소멸자 MyObject("<< name <<") : " << this << endl;

if (fdynm) { // name을 저장할 공간을 new로 만들었다면 삭제 한다.
cout << " delete name = " << name << endl;
delete [] name;
name = NULL;
}}int MyObject::counter = 0; // static은 각 객체마다 저장공간이 확보 되지 않는다.
                            // 전체에서 단 하나의 변수 공간 만이 존재한다.
                            // public이므로 어디에서나 접근이 가능하다.char* MyObject::getName() { return name; }void MyObject::setName(char *n, int opt){
if (fdynm) delete [] name;
name = n;
fdynm = opt;}/// 파일 : main.cpp //////////////////////////////////#include <iostream>#include "MyObject.h"using namespace std;//// 전역변수 //////////////////MyObject gMe(19, "홍길동"); // 객체를 생성하고, 생성자2을 호출 한다.int main(int argc, char* argv[]){
cout << "main() 시작.\n";

MyObject *pm = &gMe;

MyObject::counter++;
cout << MyObject::counter << " " << pm->getName() << " / "
 << pm->getAge() << endl;

MyObject kim; // 객체를 생성하고, 생성자1을 호출 한다.
pm = &kim;
char *pname = new char[50];
strcpy(pname, "이순신");
pm->setName(pname, 1);
pm->setAge(33);

pm->counter++;
cout << MyObject::counter << " " << pm->getName() << " / "
 << pm->getAge() << endl;

pm = new MyObject(); // 객체를 동적으로 만든다. 생성자1 사용.
pm->setName("세종대왕");
pm->setAge(33);

MyObject::counter++;
cout << MyObject::counter << " " << pm->getName() << " / "
 << pm->getAge() << endl;
delete pm;

cout << "main() 끝.\n";

return 0;}
실행결과 예:
+생성자2 MyObject(19,홍길동) : 00FDD174
main() 시작.
1 홍길동 / 19
+생성자1 MyObject() : 0031F9C4
2 이순신 / 33
+생성자1 MyObject() : 004D4948
3 세종대왕 / 33
-소멸자 MyObject(세종대왕) : 004D4948
main() 끝.
-소멸자 MyObject(이순신) : 0031F9C4
   delete name = 이순신
-소멸자 MyObject(홍길동) : 00FDD174

멤버 변수

클래스 내부의 변수를 말 한다. 클래스의 속성(attribute) 이다.
기본 자료형과 다른 클래스 들도 정의가 가능하다.
멤버함수는 프로그램 코드이므로 결국 기계어 코드 묶음에 존재한다. 멤버함수가 실행될 때, 객체의 변수들로 구성된 메모리 구조체의 시작 주소값으로 전달되어 조작된다.

멤버 함수

클래스의 행위(behavior)를 정의한 것으로 기능적으로 설정된 프로그램 묶음이다.

static 멤버 함수

this가 객체의 위치 주소값을 취급하는 자동 포인터로써 멤버함수가 호출되면 자동으로 따라 다닌다. 이말은 이미 객체가 존재 한다는 것이다. 그러나 static을 사용하면 특정 객체의 this을 사용하지 않는다. 객체가 이미 존재하는 것은 상관없이 이미 존재하는 객체의 포인터 값을 모른다. 따라서 호출 할때, 특정 객체가 미리 생성되지 않아도 된다. 이미 생성 되었다면 오히려 인수로 넘겨 주어야 한다.
class A {public:
   A(int n) { num = n; }
   int num;
   int add(int n2) { return (num += n2); }

   static int add(A *pa, int n2);};int A::add(A *pa, int n2){
    //num += n2;  // 오류 : this을 사용할 수 없다. 객체의 포인터가 넘어오지 않는다.
 // error C2597: illegal reference to non-static member 'A::num'
    pa->num += n2;
    return pa->num;}int main(){
    A a(10);
    int r = A::add(&a, 20);     // 결과 30
    std::cout << r << std::endl;

    r = a.add(&a, 10);           // 결과 40
    std::cout << r << std::endl;

    return 0;}
위의 프로그램 예제 코드 처럼, 객체가 없는 상태에서 실행되는 것이 static 멤버함수이기 때문에 객체의 멤버변수를 사용하지 않아도 된다. 클래스 이름으로 호출하면 되고 설령 이미 존재하는 객체의 멤버변수로 하더라고 this 가 넘어가지 않으므로 별 차이가 없다. 캡슐화 되지 않는 C 언어의 함수와 별 차이가 없다. C 언어의 함수와의 차이는 단지 '어느 클래스에 속하는냐'만의 의미만 갖는다.

struct(구조체) 와 class(클래스)는 동일

흔히 구조체는 클래스 이전에 선언되고 주로 여러종류의 기억공간을 선언하기 위한 형태로만 보는 경우가 일반적인데, 이는 틀린 생각이다. 구조체 역시 클래스와 동일하게 생성자와 소멸자를 지정할 수 있고, 코딩이 가능하며 내부호출과 캡슐화가 가능하다. 따라서, 하나의 물체(Object) 를 하나의 구조체(struct) 로 선언하고 사용할 수 있다. 따라서, 구조체 내에서도 모든 함수와 명령을 클래스와 동일하게 사용 가능하다. 다만, 멤버에 대해 접근 지정자를 써 주지 않는 경우 구조체는 public이 되고, 클래스는 private이 된다는 차이가 있다.