이 글은 C++기초플러스(5판)에서 발췌한 글입니다. 
 
실전 프로그래밍 : 조건 표현식의 연산자와 버그 예방법

'같다' 연산자(==)를 사용할 곳에 대입 연산자(=)를 입력하는 실수를 없애기 위하여,
많은 프로그래머들이 더 직관적인 표현인 'variable == value' 를 'value == variable'로 뒤집어 사용하고 있다.
예를 들면, 조건을 다음과 같이 입력하면

 if (3 == nCount)

바르게 동작한다. 그러나 실수로 다음과 같이 입력하면

if (3 = nCount)

리터럴에 어떤 값을 대입하여는 시도로 간주되어 컴파일러가 에러 메시지를 내보낼 것이다.
(3은 언제까지나 3일 뿐이며, 다른값을 대입할 수 없다.) 
그러나 'variable == value'형식을 사용하면서 다음과 같이 == 대신 = 를 입력하는 실수를 범했을 때에는 

if (nCount  = 3)

컴파일러가 nCount에 무작정 값 3을 대입할 것이고, 이 조건은 참이되어 if의 true 블록을 실행할 것이다.
이것은 매우 흔하게 발생하는 에러이지만 찾아내기가 무척 어렵다.
일반적으로, 컴파일러가 에러를 쉽게 찾을 수 있도록 프로그램을 작성하는 것이 찾기 어려운 에러를 만들어 놓고 이를 고치느라 애를 먹는것보다 훨씬 낫다. 

=======================

요약할께요.
if문 쓸때 'if ( nCount == 3)' 이렇게 쓰는것보다 'if (3 == nCount)로 쓰는버릇을 들이는게 좋다 입니다. 

if (nCount = 3) 으로 하면 문법적으로 문제가 없기 때문에 에러검출이 되지 않습니다.
학생분들이야 코드 몇줄 안되지만, 실무로 가면 이걸 일일히 찾아서 수정하기는 어려운 일입니다. 
 
예로 (출력부분에서 예가 잘못되었지만 조건식을 중점으로 든 예이므로 이해 부탁드립니다)

int MyAge = 20;
if (MyAge = 28)

    cout << "내 나이는 28 이다" << endl;

else
{
    cout << "내 나이는 20 이다." << endl;
}

이렇게 하면
"내 나이는 28이다" 가 출력되어 버립니다. 의도에 맞지 않는거죠.
if문에서 '='을 두번 넣는걸 실수로 한번 넣어서 생긴 오류입니다. 하지만 문법적으로 문제는 없죠.
컴파일러가 에러를 잡아주지 않기 때문에 뭐가 문제지? 하면서 오랜시간 디버깅을 해야할수도 있고, 재수좋아서 한방에 끝낼수도있습니다만, 애초에 에러를 안내기 위해서는

int MyAge = 20;
if ( 28 == MyAge)

    cout << "내 나이는 28 이다" << endl;

else
{
    cout << "내 나이는 20 이다." << endl;
}

로 하면  if문의 조건식에서 false를 리턴하기 때문에 if문 안으로 들어가지 않습니다. 그래서 "내 나이는 20 이다"가 출력되는게 정상이죠.

즉, 변경할수 없는 값, 상수값을 앞에 둬야 실수로 '='으로 입력했더라도 컴파일러 수준에서 에러를 검출해 줄수 있습니다. 

ps. 언어마다 다릅니다. java같은 경우는 c와 다른 조건식의 경우를 가집니다.
java는 if문의 조건식에서 대입을 할수 없는것으로 알고있습니다.
이 글은 c와 c++의 경우로 한정하고 있습니다.(제가 아는 범위 한정입니다. 제가 틀렸을경우 신속한 지적 부탁드립니다
삽질하다 결국 ㅈㅈ 쳤는데 알고보니 다 왔었다 참나 ㅋㅋㅋㅋ

솔루션 하나를 만들고, MFC용 프로젝트와 DLL을 만들 DLL프로젝트를 만든다.
그리고 MFC에 DLL과의 연결 속성들을 알아서 정해준다. (뭐 그런것들... Projects Dependencies...zz)
그런것들은 알아서 정해주고 이제 각 DLL을 구성하는 헤더들에 같은 선언들을 해준다.

//Component.h
#ifdef DLLCOMPONENT
#define CCOMPONENT __declspec(dllexport)
#else
#define CCOMPONENT __declspec(dllimport)
#endif

class CCOMPONENT CComponent{ ~~~~~~~};
 
//Component.cpp
#define CCOMPONENT //이건 해주는지 안해주는지 까먹었다. 알아서 하셈
#include <iostream> 
~~~~ 

이런식으로 선언을 모든 DLL구성 파일들에게 해줘야 한다. "똑같이"
즉, DLL로 만들 클래스들을 저런식으로 똑같이 선언해주면 된다는 거다.

젠장.....
난 클래스마다 다르게 했더만 아니라네? ㅠㅠ
클래스 이름이 달라서 다르게 구분지어 줬더만 똑같이하라니 이런 된장 ㅠㅠㅠㅠㅠ 

추가. 이거 삽질한다고 각종 리크에러들 다 나왔음 ㅠㅠ 
출처 : http://nicejinux.net/bbs/zboard.php?id=lecture&no=66
-- 감사합니다

using System;        
using System.Diagnostics;        
using System.Windows.Forms;        
using System.Runtime.InteropServices;        

class InterceptKeys        
{        
        private const int WH_KEYBOARD_LL = 13; 
        private const int WM_KEYDOWN = 0x0100; 
        private static LowLevelKeyboardProc _proc = HookCallback;        
        private static IntPtr _hookID = IntPtr.Zero; 

        public static void Main()        
        {        
                _hookID = SetHook(_proc);        
                Application.Run(); 
                UnhookWindowsHookEx(_hookID);        
        }        

        private static IntPtr SetHook(LowLevelKeyboardProc proc) 
        {        
                using (Process curProcess = Process.GetCurrentProcess()) 
                using (ProcessModule curModule = curProcess.MainModule)        
                {
                        return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0); 
                }        
        }        
        
        private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);        
        private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) 
        {        
                if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
                {        
                        int vkCode = Marshal.ReadInt32(lParam);        
                        Console.WriteLine((Keys)vkCode); 
                }        
        
                return CallNextHookEx(_hookID, nCode, wParam, lParam); 
        }
        
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);   
        
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
        [return: MarshalAs(UnmanagedType.Bool)] 
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);   
        
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); 
        
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
        private static extern IntPtr GetModuleHandle(string lpModuleName);  
}


콘솔어플리케이션으로 만들고 system.window.form하나 추가해주고 
프로젝트 만들때 MFC_Test라는 이름으로 만들었다면 MFC_TestDlg.cpp파일에서 #include 부분 바로 다음에 이걸 추가하시고
#ifdef _DEBUG
#pragma comment(linker, "/entry:WinMainCRTStartup /subsystem:console")
#endif
다음에 본인이 필요한 부분에
printf("인덱스 값은 %d" , m_nIndex); 
를 추가해서 컴파일 하면 어플리케이션 창과 콘솔창이 동시에 뜨는것을 확인할수 있을것이고,
입력이나 수정, 삭제시마다 실시간으로 확인이 가능합니다.


ps. 음하하하하 한문장으로 다 썼다 ㅋㅋㅋ
먼저 VS2003으로 작성.
기본적인 MFC 어플리케이션 프로젝트로 생성 후 
Edit Control을 두개 넣고 이름 지정(IDC_EDIT1,2로 지정)해주고,
확인으로 지정해줄 버튼을 생성 후 두번 클릭하면 자동으로 OnBnClickedButton1()이 생성된다. (IDC_BUTTON1)


그리고 그것에서

GetDlgItemText(IDC_EDIT1, m_str1); 
GetDlgItemText(IDC_EDIT2, m_str2);  

MessageBox(""아이디는 : " + m_str1 + "\n" + "비밀번호는 : " + m_str2, "입력한 값", MB_OK);

으로 입력한 후 실행하면 됨.

ps. m_str1, m_str2는 CString으로 미리 선언해뒀음 
int main()
{
CComponent* pComponent = new CComponent("My Component");
  
        //1. 객체로 인자를 넘길때(1번을 한다면 2번을 주석처리하세요)
        pComponent->AddParameter(CParameter("Test1"));
pComponent->AddParameter(CParameter("Test2"));
pComponent->AddParameter(CParameter("Test3"));
pComponent->AddParameter(CParameter("Test4"));
pComponent->AddParameter(CParameter("Test5"));
pComponent->AddParameter(CParameter("Test6"));
pComponent->AddParameter(CParameter("Test7"));
//////// 

 
       //2. 포인터로 인자를 넘길때(2번을 한다면 1번을 주석처리하세요)
for (int i = 0; i < 6; i++)
{
pComponent->AddParameter(CParameter("Test"));
}
//////// 
int nSize = pComponent -> GetParameterSize();
        for (int i = 0; i < nSize; i++) //객체로 넘길때입니다. 포인터로 넘길시에 "GetParameterAt(i)->"로 변경.
        //아래의 포문도 동일
{
cout << i+1 << " " << pComponent->GetParameterAt(i).GetName() << endl;
}
cout << endl;

cout << "3번째 객체를 삭제합니다" << endl;

pComponent->DeleteParameterAt(3);
cout << endl;
nSize = pComponent -> GetParameterSize();
for (int i = 0; i < nSize; i++)
{
cout << i+1 << " " << pComponent->GetParameterAt(i).GetName() << endl;

}
cout << endl;

delete pComponent;

return 0;
}


헤더파일 분리
Component.h , Parameter.h, BaseEntity.h 로 분리후 Parameter클래스는 BaseEntity클래스를 상속받음.
BaseEntity클래스는 "Test1", "Test2" 같은 문자를 입력받습니다.(멤버변수가 'char* 변수명'임) 
Component클래스는 Parameter객체들을 배열형태로 관리 합니다.

구현해보시고 제가 만든거하고 비교해보세요 
void plus(char* a, char* b, char*& sum)
{
sum = strcat(a, b);
}

int main()
{
        char a[100] = { '\0' };
strcpy(a, "Hello ");

char* sum = 0;

plus(a, "World!", sum);

cout << sum << endl; 



출력 >> Hello World!


이건 call-by-value일까요 call-by-reference일까요?


ps. call-by-address보다는 주소값을 이용한 call-by-reference가 맞는것 같습니다.... 

(아래 0- 로 시작하는 과제는 C 표준 함수를 사용하지 않고 포인터 연산을 통해 구현한 버전과 C 표준 함수를 사용한 두 버전을 각각 구현하시오.)

 
========================================================================
0-1. 문자열을 더하는 함수를 작성하시오. 
 
char* GetAppendedString(const char* source, const char* toAppend) {
    ...    
}
 
main(...) {
    cout << GetAppendedString("Hello", "World"); // HelloWorld
}
========================================================================
0-2. 문자열을 비교하는 함수를 작성하시오. 
 
bool IsEqualString(const char* source, const char* target) {
    ...
}
 
main(...) {
    cout << IsEqualString("Hello", "World"); // false
}
========================================================================
0-3. 문자열을 복사하는 함수를 작성하시오. 
char* CopyString(char* dst, const char* src) {
    ...
}
 
main(...) {
    char szValue[80];
    ...
    CopyString(szValue, "HelloWorld");
    cout << szValue; // HelloWorld
}
========================================================================
0-4. 문자열을 특정 문자로 분리하여 배열을 반환하는 함수를 작성하시오.
 
?? Tokenize(char* szValue, char* token, ???) {
    ...
}
 
main(...) {
    char* value = "ABC,DEF,GHI";
    
    ?? = Tokenize(value, ',');
    for (int i = 0; i < ??; i++) {
        cout << ??[i] << endl;
    }
    // ABC
    // DEF
    // GHI
}
========================================================================
 
 
========================================================================
1. 문자열과 숫자를 더하여 문자열을 반환하는 함수를 작성하시오.
 
char* GetNumericAddedString(const char* arg_szValue, int arg_nNum)
{
 
}
========================================================================
2. 문자열에 특정 범위 내의 숫자를 더한 후 배열로 반환하는 함수를 작성하시오.
 
?? GetStringArray(const char* arg_szValue, int arg_nBeginIdx, int arg_nEndIdx)
{
    return ??
}
 
main(...)
{
    ?? pArr = GetStringArray("Test", 0, 7);
    for (int i = 0; i <= 7; i++) {
        cout << pArr[i] << endl;
    }
 
    // Test0, Test1, Test2, ... Test7
}
========================================================================
3. 위 2번에서 반환된 ??의 메모리를 해제하는 코드를 작성하시오.
========================================================================
아래와 같은 상속구조를 갖는 클래스들을 정의하고 이들의 업무를 관리하는 근로자센터 클래스를 구현하시오

           사람
             |
          근로자
===================
|           |             |
군인    의사       개발자

1. 사람 : 이름, 나이를 속성으로 포함

2. 근로자 : 근무지를 속성으로 포함. 업무를 행위로 포함( DoWork() 함수를 정의)

3. 군인 : 병과 (육군/해군/공군)을 속성으로 포함. 행위 수행시 " ~~ 근무지에서 ~~ 병과로 나라를 지키다"
4. 의사 : 전공(내과/외과 등)을 속성으로 포함. 행위 수행시 "~~ 근무지에서 ~~ 전공으로 환자를 치료하다"
5. 개발자 : 기술(C++/JAVA 등)을 속성으로 포함. 행위 수행시 "~ 근무지에서 ~ 기술로 개발을 하다."

[제약사항]
1. 사람과 근로자는 개별로 생성할 수 없다.
2, 근로자 센터는 군인, 의사, 개발자 드으이 객체들을 리스트 형태로 관리하며, 기본적으로 Add(...), DoWorkAll(...)함수를 정의해야 한다.


main()
{
WorkerCerter* pCenter = new WorkerCenter();

~~ pDoctor = new Doctor("Kim", 25, "인천" , "내과");
~~ pSoldier = new Soldier("Lee" , 30, "서울", "육군");
~~ pDeveloper = new Developer("Hong", 19, "부산", "C++");

pCenter->Add(pDoctor);
pCenter->Add(pSoldier);
pCenter->Add(pDeveloper)

/*
아래 호출시 아래가 출력되어야 함
나이 25세 kim이 인천 근무지에서 내과 전공으로 환자를 치료하다.
나이 30세 Lee이 서울 근무지에서 육군병과로 나라를 지키다.
나이 19세 Hong이 부산 근무지에서 c++기술로 개발을 하다.
*/

pCenter->DoWorkAll();
}



/////////////////////


이렇게 하는게 문제입니다.
각 클래스는 사람, 근로자, 군인, 의사, 개발자 그리고 근로자 센터 클래스 이렇게 6개가 되겠으며
사람 클래스와 근로자 클래스는 추상클래스로 객체 선언이 불가능하게 만들어야 합니다.(선언하면 에러나게..무슨뜻인지 아시죠?)

그리고 근로자센터가 이들을 리스트로 연결해서 출력까지 하게 만들어야 합니다.
또 파일들을 분리해서 작성하세요. main.cpp에 다 넣어서 복잡하게 만들지 마시고 ㅋ


일단 개인적으로 만들어 보시고 제가 ㅁ나든것과 비교해 보세요.
여러분이 더 잘만드셨을껍니다. ㅠㅠㅠ

전 템플릿도 쓰지 않았습니다. 단지 c++로 클래스와 상속, 가상함수 들을 이용한 부분까지만 사용했습니다.
열공합시다!! ㅋ
c++에서 구조체와 클래스의 차이는 무엇일까?

구조체는 기본접근자가 public이고 클래스는 기본접근자가 private이다.

즉,
struct temp
{
int       a;
.....
};
일때는 int a가 public처럼 외부에서 접근 가능하지만,

class temp
{
int      a;
public:
int     c;
};
처럼  int a는 기본이 private라는 소리이며,
 public이라는 접근자를 적어줘야 int c가 public으로 인식되서 외부에서 접근이 가능하다는 뜻이다.

이는 정보은닉과도 관련이 있........나?
그것까지는 생각 안해봤음

일단 분명한것은 이것임. 기본접근자에 대한 차이

+ Recent posts