배열

1. 배열 원소의 개수를 쓸 때는 반드시 상수가 아닌 sizeof(array) / sizeof(array[0])을 쓰기

만약 10개짜리 배열을 선언 후 for문에서 10번 반복하게 해뒀는데 해당 배열의 선언부를 5로 수정하게 되면 6~10까지 접근 위반이 생기기 때문

반복할 때마다 해당 식을 계산하는 오버헤드를 걱정한다면 캐시메모리의 존재를 생각해볼 것.

 

 

동적할당

1. 배열이나 구조체같은 큰 단위는 동적할당을 사용하자

동적할당시 반드시 해제해줘야한다. 그리고 해제 후에는 포인터값을 0으로 초기화해준다.

int* p = new int;

*p = 10;

delete p; p = NULL;

만약 마지막에 p값을 return해서 다른 함수로 반환해준다면 그 받은 함수에서만 delete해주면 같은 주소값이니 한번으로 해제되는 것.(여러번 해제할 필요 없음)

 

2. 생성자 내에서 동적할당을 사용하는 경우 소멸자에서 delete 해줘야한다.

생성자 내에서 new가 나오면 자연스레 소멸자에 delete부터 쓰고 시작하기

객체 자체를 동적할당으로 사용하는건 그냥 객체 다 쓰고나면 delete하면 되는거지만, 생성자 내에서 특정 변수를 동적할당으로 사용하는 경우를 얘기하는 것임

 

 

포인터

1. 포인터 변수는 항상 0으로 초기화한다. 포인터 변수를 사용하기 전에는 0이 아닌지 비교한다.

int *p=null;

if(null != p) ~~~

 

2. 포인터값은 16진수로 표시를 한다. 또한 포인터값은 컴파일러의 비트에 따라 4바이트/8바이트로 나뉜다.

컴파일러가 32비트라면 4바이트로, 컴파일러가 64비트라면 8바이트로 사용된다.

운영체제가 아니라 컴파일러임을 주의!

그러므로 포인터는 0xffffffff 이런식으로 표시가 되며 포인터는 꼭 %x를 이용해 표시하자

 

 

헤더파일

1. 구조체는 전부 다 헤더파일에 작성한다.

 

2. A.cpp에는 반드시 A.h를 포함시킨다

파일을 여러개로 나눠서 작성할 때 자기 파일에 자기 헤더를 포함시켜준다.

이걸로 해결할 수 있는 문제가 많은데 그냥 습관화해서 미리 다 해결해버리면 된다.

 

 

함수

1. 값을 바꾸지 않는 함수는 const함수로 만든다

void Print() const; 처럼 만들어서 값을 바꾸지 않음을 명시화해주는 것이 좋음

get함수류는 일단 다 const로 만들어주기

 

2. 배열을 인자로 넘겨주는 함수는 배열원소의 갯수도 같이 넘겨주는것이 좋다.

만약 int ary[5]가 선언되어 있으면 sizeof(ary)라고 하면 배열 전체의 크기인 20바이트가 나옴

그런데 int *ap = ary;일때 sizeof(ap)라고 하면 포인터변수인 ap의 크기이니까 포인터변수 크기인 4바이트가 나옴

이게 뭐가 문제가 되냐면 호환성을 위해서 배열인자 갯수가 변할때를 대비해서 코드를 sizeof(ary)/sizeof(ary[0]) 이런식으로 짤때가 있는데

근데 만약 포인터변수인 ap면 저거로 계산이 불가능해져버림

그래서 호환성을 위해선 아예 애초에 매개변수로 배열인자갯수도 넘겨주게 코드를 짜는게 좋게됨

ary_prn(ary, sizeof(ary)/sizeof(ary[0])); 이런식으로 배열명이랑 배열인자갯수 둘다 넘겨주는 코드 자주 보였던 이유가 위와 같은 이유.

 

 

예외처리

1. 생성자내에서 예외가 발생할때, 예를들어 생성자 내에서 동적할당을 한 후에 생성자가 끝나기 전에 예외가 발생해서 생성자가 비정상종료되면 객체가 생성되지 않았다고 판단해서 소멸자도 호출되지 않음. 그럼 메모리릭이 발생하므로 생성자에서 new를 쓰면 try로 감싸주고 catch문 안에서 delete해준 후 예외를 다시 던져서 다른 함수로 전달해주던가 하면 됨.

스마트포인터나 unique_ptr을 쓰는 방법도 있음

 

2. 소멸자에서는 예외를 절대로 밖으로 보내면 안된다.

소멸자를 호출한 함수가 따로 있는게 아니다보니 소멸자에서 예외를 밖으로 보내면 프로그램이 강제종료되므로 소멸자 내부의 내용을 전부 다 try로 감싸주고

catch(...) {} 로 소멸자 안에서 처리해줘야한다

 

3. new나 new[]를 사용할 때는 반드시 예외처리를 해줘야한다.

메모리가 모자라면 bad_alloc 예외가 발생하기때문에

#include <new>에 catch(bad_alloc& ex) {}로 예외 처리를 해줘야 함(ex.what();함수로 예외내용을 볼 수 있음)

 

4. 예외로 객체를 throw하는 경우 해당 객체를 받는 catch문에서 부모객체가 자식객체보다 아래에 가도록 작성해야한다

try {

    func(c);

  } catch (Parent& p) {

    ...

  } catch (Child& c) {

    ...

  }

만약 이런식으로 작성하면 자식객체는 부모클래스에 들어갈수 있기때문에 자식클래스가 throw됐음에도 윗쪽의 catch문이 실행된다.

그러므로 객체를 throw하는 경우 부모클래스가 더 아래에 가야한다.

 

 

가상함수

1. 클래스에 단 하나의 가상함수라도 있다면 소멸자도 가상함수로 만들어야한다.

생성자는 부모클래스의 생성자 먼저, 그 다음 자식클래스의 생성자가 호출

소멸자는 자식클래스의 소멸자가 먼저, 그 다음 부모클래스의 소멸자가 호출되어야 하는데

소멸자도 오버라이딩된거기때문에 가상함수가 아니면 부모꺼만 호출되고 자식꺼가 먼저 호출이 불가능함

 

 

define

1. 매크로의 경우 그대로 치환되는 것이니까 만약 #define PLUS(x, y) x+y 이런식으로 해버리면 공식같은 곳에 가서 연산순서때문에 

더하기빼기나 곱하기 나누기 순서가 뒤바뀌어서 원하는 순서가 안나올 수 있는 위험이 있음. 

그래서 매크로로 공식을 만들땐 반드시 모든 인자에 괄호를 씌워서 우선순위를 설정해야 함 

#define PLUS(x, y) ((x)+(y)) 이런식으로

 

 

조건문

1. switch문에 case들을 쓸 때 case에 내용이 여러줄일 경우 break;도 포함해서 괄호로 감싸준다.

마지막 case나 default에도 break;는 써준다(C++에선 써줘도 안써줘도 되지만 C#에선 반드시 써줘야하기때문에 그냥 쓰는 습관 들인다)

 

2. dangling else문제를 해결하기 위해서라도 if문 밑에 코드가 한줄뿐이더라도 중괄호로 묶어주는게 오류예방에 좋을 수 있다.

 

3. else if문에서 주의사항

if(avg >= 90) grade = 'A'; else if(avg >=80) grade = 'B'; 

이런식으로 쓰면 되는걸 80~90이면 B로 쓸 필요가 없다. 이미 90넘는애들은 다 위에서 걸러졌으니 여기선 그냥 80이상만 거르면 된다.

 

 

임시변수

1.

void main ()

{

    int a, b;

    {

        int temp;

        temp = a;

        a=b;

        b=temp;

    } 

{    

이런식으로 사용하게 되면 temp변수는 괄호가 열릴때 생성되서 괄호가 닫힐때 회수됨. 

그러므로 temp변수는 잠깐 사용하는거니까 저렇게 쓰는게 효과적일 수 있음

 

 

나눗셈

1. 나눗셈은 특별하다. 0으로 나눌 수 없고, 형변환을 고려해야한다

 

 

단락회로평가

1. &&라던가 ||같은게 보이면 단락회로평가 떠올리기

 

 

구조체와 클래스

1. 구조체와 클래스 모두 세미콜론으로 끝낸다

 

2. 디폴트생성자 외의 생성자를 하나라도 만들거면 디폴트생성자도 반드시 같이 만들어줘야 한다.

다른 생성자 한개라도 생기는순간 디폴트생성자가 적용이 안된다