20141103 (C++ Template 특수화, 부분 특수화, 인자, static, 예외처리, ARM PWM)

171일차









----------

C++

----------



------- Template 특수화, 부분 특수화


템플릿을 특수화하면 템플릿을 구성하는 멤버를 다르게 행동하도록 할 수 있다.

다음 예제 참고


예제)

#include <iostream>

#include <cstring>

using namespace std;


template <typename T>

class Point 

{

private:

T xpos, ypos;

public:

Point(T x=0, T y=0): xpos(x), ypos(y)

{  }

void ShowPosition() const

{

cout<<'['<<xpos<<", "<<ypos<<']'<<endl; 

}

};


template <typename T>

class SimpleDataWrapper 

{

private:

T mdata;

public:

SimpleDataWrapper(T data) : mdata(data)

{ }

void ShowDataInfo(void)

{

cout<<"Data: "<<mdata<<endl;

}

};


template<>

class SimpleDataWrapper <char*>

{

private:

char* mdata;

public:

SimpleDataWrapper(char* data)

{

mdata=new char[strlen(data)+1];

strcpy(mdata, data);

}

void ShowDataInfo(void)

{

cout<<"String: "<<mdata<<endl;

cout<<"Length: "<<strlen(mdata)<<endl;

}

~SimpleDataWrapper()

{

delete []mdata;

}

};


// Template 특수화

template<>

class SimpleDataWrapper <Point<int>>

{

private:

Point<int> mdata;

public:

SimpleDataWrapper(int x, int y) : mdata(x, y)

{ }

void ShowDataInfo(void)

{

mdata.ShowPosition();

}

};


int main(void)

{

SimpleDataWrapper<int> iwrap(170);

iwrap.ShowDataInfo();


SimpleDataWrapper<char*> swrap("Class Template Specialization");

swrap.ShowDataInfo();


SimpleDataWrapper<Point<int>> poswrap(3, 7);

poswrap.ShowDataInfo();

return 0;

}



위의 예제에서 확인할 수 있듯이 템플릿을 하면 컴파일러에서 자동으로 다른 타입의

함수를 생성해 주지만 특수한 경우는 따로 선언한 것을 사용하게 한 것을 특수화라 한다.





--- Template 부분 특수화


특수화와 같은데 쬐금 업그레이드(?) 된 것이다.


예제)

#include <iostream>

using namespace std;


template <typename T1, typename T2>

class MySimple 

public:

void WhoAreYou()

{

cout<<"size of T1: "<<sizeof(T1)<<endl;

cout<<"size of T2: "<<sizeof(T2)<<endl;

cout<<"<typename T1, typename T2>"<<endl;

}

};


template<>

class MySimple<int, double>

{

public:

void WhoAreYou()

{

cout<<"size of int: "<<sizeof(int)<<endl;

cout<<"size of double: "<<sizeof(double)<<endl;

cout<<"<int, double>"<<endl;

}

};



template<typename T1>

class MySimple<T1, double>

{

public:

void WhoAreYou()

{

cout<<"size of T1: "<<sizeof(T1)<<endl;

cout<<"size of double: "<<sizeof(double)<<endl;

cout<<"<T1, double>"<<endl;

}

};



int main(void)

{

MySimple<char, double> obj1;

obj1.WhoAreYou();

MySimple<int, long> obj2;

obj2.WhoAreYou();

MySimple<int, double> obj3;

obj3.WhoAreYou();

return 0;

}

결과














------- Template 인자


템플릿 매개변수의 선언에 마치 함수처럼 변수의 선언을 할 수 있다.


예제)

#include <iostream>


using namespace std;


template <typename T, int len> // 인자 선언

class SimpleArray

{

private:

T arr[len]; // 인자만큼 배열 할당


public:

T& operator[] (int idx)

{

return arr[idx];

}


SimpleArray<T, len>& operator=(const SimpleArray<T, len>& ref)

{

for(int i = 0; i < len; ++i)

{

arr[i] = ref.arr[i];

}

return *this;

}

};


int main()

{

SimpleArray<int, 5> i5arr1; // 객체에 int * 5 크기의 배열 멤버 할당

for(int i = 0; i < 5; ++i)

{

i5arr1[i] = i * 10;

}


SimpleArray<int, 5> i5arr2; // 객체에 int * 5 크기의 배열 멤버 할당

i5arr2 = i5arr1;

for(int i = 0; i < 5; ++i)

{

cout << i5arr2[i] << ", ";

}

cout << endl;


SimpleArray<int, 7> i7arr1; // 객체에 int * 7 크기의 배열 멤버 할당

for(int i = 0; i < 7; ++i)

{

i7arr1[i] = i * 10;

}


SimpleArray<int, 7> i7arr2; // 객체에 int * 7 크기의 배열 멤버 할당

i7arr2 = i7arr1;

for(int i = 0; i < 7; ++i)

{

cout << i7arr2[i] << ", ";

}

cout << endl;


return 0;

}

결과




설마 생각한 그것도 가능하다.

바로 인자에 디폴트 값 지정....


예제)

#include <iostream>


using namespace std;


template <typename T = int, int len = 7>

class SimpleArray

{

private:

T arr[len];

public:

T& operator[] (int idx)

{

return arr[idx];

}

T& operator=(const T&ref)

{

for(int i=0; i<len; i++)

arr[i]=ref.arr[i];

}

};


int main(void)

{

SimpleArray<> arr; // 아무 값도 안넣어 줬기 때문에 디폴트 값이 들어감.

for(int i = 0; i < 7; i++)

arr[i] = i + 1;

for(int i = 0; i < 7; i++)

cout << arr[i] << " ";

cout << endl;


return 0;

}

결과











------- Template과 static 멤버 변수


다음 예제 참고


예제)

#include <iostream>


using namespace std;


template <typename T>

class SimpleStaticMem

{

private:

static T mem; // static 변수 선언. 같은 타입끼리 공유함.


public:

void AddMem(int num)

{

mem = mem + num;

}


void ShowMem()

{

cout<<mem<<endl;

}

} ;


template <typename T> 

T SimpleStaticMem<T>::mem = 0; // class 내에 static 멤버는 꼭 전역에서 초기화 필수!!



int main(void)

{

SimpleStaticMem<int> obj1; // 같은 클래스 이기 때문에 mem을 공유함

SimpleStaticMem<int> obj2; // 같은 클래스 이기 때문에 mem을 공유함

obj1.AddMem(2); // 2 더함

obj2.AddMem(3); // 3 더함

obj1.ShowMem();


SimpleStaticMem<long> obj3; // 같은 클래스 이기 때문에 mem을 공유함

SimpleStaticMem<long> obj4; // 같은 클래스 이기 때문에 mem을 공유함

obj3.AddMem(100); // obj3에 100 더함

obj4.ShowMem(); // obj4에서 출력했지만 공유하기 때문에 100 출력


return 0 ;

}

결과












------- 예외처리

 

try, catch, throw 라는 것으로 예외 처리를 하면 가독성과 유지보수성을 높일 수 있다.


예제)

#include <iostream>


using namespace std;


int main()

{

int num1;

int num2;


cout << "두 개의 숫자 입력: ";

cin >> num1 >> num2;


try

{

if(num2 == 0)

{

throw num2;

}

cout << "나눗셈의 몫: " << num1/num2 << endl;

cout << "나눗셈의 나머지: " << num1%num2 << endl;

}

catch(int expn) // throw num2 값을 expn이 받음

{

cout << "제수는 " << expn << "이 될 수 없습니다." << endl;

cout << "프로그램을 다시 실행하세요." << endl;

}


cout << "end of main" << endl;


return 0;

}






















------------

ARM

------------


------- PWM


이번엔 TC device가 아닌 PWM device를 사용한다.






- Clock 주파수 신호 종류

   총 13가지 이다. 이 중에 선택하여 사용함.




- Timing도





Timing도를 설명하자면









예제 소스)

/* 14번 Pin에 물린 LED가 점점 어두워짐. */

#include "AT91SAM7S256.h"


#define MAX_DUTY 800

#define DFT_DUTY 100


void PWM3_Init(void);

void Delay_ms(unsigned int ms);

void PWM3_Duty(unsigned int uiDuty);


int main(void)

{

volatile unsigned int uiCnt;

volatile unsigned int uiDuty;

PWM3_Init();

uiDuty = DFT_DUTY;

while(1)

{

for(uiCnt = 0; 100000 > uiCnt; ++uiCnt); // 이 딜레이로 uiDuty의 값이

PWM3_Duty(uiDuty); // 변하는 시간이 된다. 이 시간이 길수록

// 바뀌는 시간도 느려진다.

if(uiDuty < MAX_DUTY)

{

uiDuty = uiDuty + 10;

}

else

{

uiDuty = DFT_DUTY;

}

}

return 0;

}




void PWM3_Init(void)

{

*AT91C_PMC_PCER = (1 << AT91C_ID_PWMC);  // PWM 전원 공급

*AT91C_PIOA_PPUDR = AT91C_PA14_PWM3; // Pull-up off

*AT91C_PIOA_BSR = AT91C_PA14_PWM3; // PWM3 output(Peripheral B모드 사용)

*AT91C_PIOA_PDR = 0xFFFFFFFF; // 모든 PIO비활성화


*AT91C_PWMC_MR = (0x00 << 24) | // PREB = MCK

(0x06 << 8) | // PREA = MCK/64

(0 << 16)  | // DIVB = Off

(30 << 0); // DIVA = 30

// 48Mhz / 64 = 750Khz

// 750Khz / 30 = 25Khz


*AT91C_PWMC_CH3_CMR = (0 << 8) | // CALG = 0, 톱니모양

(0 << 9) | // CPOL = 0, Low로 시작

(0 << 10)| // CPD = 0, duty

(0x0B); // CPRE = CLKA


*AT91C_PWMC_CH3_CDTYR = DFT_DUTY; // TC0 RA와 같은 기능

*AT91C_PWMC_CH3_CPRDR = MAX_DUTY; // TC0 RC와 같은 기능

*AT91C_PWMC_ENA = AT91C_PWMC_CHID3; // PWM Enable

}




void PWM3_Duty(unsigned int uiDuty)

{

if(MAX_DUTY < uiDuty)

{

uiDuty = MAX_DUTY;

}

*AT91C_PWMC_CH3_CUPDR = uiDuty;

// 출력 주파수 조절하기 위해 CDTY Reg를 바로 건들면 오류가 난다.

// CUPD Reg의 값을 바꾸면 다음 주기때 CDTY값이 바뀐다.

}


결과


(결과에 불 깜빡임을 보면 부드럽게 꺼지는데, 실제로 볼 경우 제법 깜빡임이 있다.)








위의 소스에서 아래와 같이 수정하면


#define MAX_DUTY (18750 * 2) // 초당 주파수 값의 두배로 줌

#define DFT_DUTY 18750 // 초당 주파수 값


int main(void)

...

while(1)

{

for(uiCnt = 0; 100000 > uiCnt; ++uiCnt); // 그냥 딜레이 걸어줌

PWM3_Duty(uiDuty); // 값을 넣어주면

// 1초씩 번갈아 가며 깜빡인다.

}

...


void PWM3_Init(void)

...

*AT91C_PWMC_MR = (0x09 << 8) | // PREA = MCK/512

(5 << 0); // DIVA = 5

// 48 Mhz / 512 = 93750 hz

// 93750 hz / 5 = 18750 hz

...


결과







설정

트랙백

댓글

20141031 (C++ Template 분할 컴파일, ARM PWM)

170일차









-----------

C++

-----------



------- Template class 분할 컴파일


다음 예제 확인


예제)

--- PointTemlate.h


#ifndef __POINT_TEMPLATE_H
#define __POINT_TEMPLATE_H

template <typename T>
class Point
{
	private:
		T xpos, ypos;

	public:
		Point(T x = 0, T y = 0);

		void ShowPosition() const;
};

#endif




--- PointTemlate.cpp


#include <iostream>
#include "PointTemplate.h"

using namespace std;

template <typename T>
Point<T>::Point(T x = 0, T y = 0) : xpos(x), ypos(y)
{ }

template <typename T>
void Point<T>::ShowPosition() const
{
	cout << "[" << xpos << ", " << ypos << "]" << endl;
}




--- main.cpp


#include <iostream> #include "PointTemplate.h" using namespace std; int main() { Point<int> pos1(3, 4); pos1.ShowPosition(); Point<double> pos2(3.1, 4.2); pos2.ShowPosition(); Point<char> pos3('P', 'T'); pos3.ShowPosition(); return 0; }





이대로 컴파일하면 에러다.

템플릿에 대한 정보가 부족해서 그렇다.


해결 방법은 두 가지로

1. main.cpp에 #include "PointTemplate.cpp" 추가

2. PointTemplate.h 에 정의 내용 추가


그럼 에러없이 잘된다.

















------------

ARM

------------




------- PWM


Pulse Width Modulation의 뜻으로 주파수 변조이다. 예를 들어 5V의 신호를

High, Low 로 밖에 처리하지 못했었는데,

만약 High인 시간이 아주 짧게 계속 반복 된다면 어떻게 될까?

그냥 깜빡거림이 짧아질까??




하지만 깜빡거림이 짧아지면서 5V의 효과가 아닌 0.5V의 효과, 전압을 제어하게 된다!!

그래서 LED의 밝기가 약해지고 밝아지고를 할 수 있게 된다.




PWM은 두 가지 방법으로 사용 가능하다

1. PWM device를 사용

2. TC device를 사용


이번엔 TC device를 사용해서 해본다.



--- 먼저 TC Block Diagram



전에는 Interrupt 발생(보라색)으로 시간 계산을 했었는데

이제는 (빨강색) 내부 clock을 가공하여 외부로 출력해 일정 전압이 발생하는 것처럼 처리한다.



각 신호에 대한 설명




- 각 핀 설명







- 주파수가 발생하는 과정?



TC_CV Reg 값이 증가하다가 Ra Reg에 설정해 놓은 값을 만나면 TIOA에 High가 걸린다.

TC_CV Reg 값이 증가하다가 Rb Reg에 설정해 놓은 값을 만나면 TIOB에 High가 걸린다.

TC_CV Reg 값이 증가하다가 Rc Reg에 설정해 놓은 값을 만나면 TIOA, TIOB에 Low가 걸린다.

TC_CV Reg 값이 증가하다가 0xFFFF 인 최대 값에 도달하면 0으로 됨.


이때 이 High 걸리는 시간에 따라 전압이 달라지게 된다.




예제 소스)

#include "AT91SAM7S256.h"


#define MASTERCLOCK     48000000


void Delay_ms(unsigned int ms)

{

volatile unsigned int count;

volatile unsigned int countmax = (MASTERCLOCK / 10000) * ms;

for(count = 0; count < countmax; count++);

}


int main(void)

{

unsigned int RA = 1000;         // initial register value

unsigned int RB = 0xFFFF;

unsigned int RC = 0xFFFF;

unsigned int uiCnt = 1000;


*AT91C_PMC_PCER = (1 << AT91C_ID_TC0); // TC0 전원 공급

*AT91C_PIOA_PDR = 0xFFFFFFFF; // PA0, PA1 PIO비활성화

*AT91C_PIOA_BSR = AT91C_PIO_PA0 | AT91C_PIO_PA1;  // TIOA0,TIOB0 output(Peripheral B모드 사용)


   // TC0 Channel Control Register

//*AT91C_TC0_CMR =0x89898402;

*AT91C_TC0_CMR = AT91C_TC_BSWTRG_NONE |

AT91C_TC_BEEVT_NONE |

AT91C_TC_BCPC_CLEAR |

AT91C_TC_BCPB_SET |

AT91C_TC_ASWTRG_NONE |

AT91C_TC_AEEVT_NONE |

AT91C_TC_ACPC_CLEAR |

AT91C_TC_ACPA_SET |

AT91C_TC_WAVE |

AT91C_TC_WAVESEL_UP |

AT91C_TC_EEVT_XC0 |

AT91C_TC_CLKS_TIMER_DIV1_CLOCK;


*AT91C_TC0_CCR = 5;


*AT91C_TC0_RA = RA; // set TC0 RA register

*AT91C_TC0_RB = RB; // set TC0 RB register

*AT91C_TC0_RC = RC; // set TC0 RC register

*AT91C_TC0_SR = AT91C_TC_CLKSTA; // TC0 clock enable and start


while(1)

{

RA = RA + uiCnt;

RB = RB - uiCnt;

Delay_ms(250);


if(RA > RC)

{

RA = 1000;      // RA(PA0)은 점점 어두워지고

RB = 0xFFFF;    // RB(PA1)은 점점 밝아진다.

uiCnt = uiCnt + 2000;    // 밝아지고 어두워지는 속도가 점점 빨라짐

if(uiCnt > RC) // 속도 초기화

{

uiCnt = 1000;

}

}

*AT91C_TC0_RA = RA;

*AT91C_TC0_RB = RB;

}

}






설정

트랙백

댓글

20141030 (C++ string class, Template)

169일차









----------

C++

----------




------- string class


문자열과 관련된 class이다.

다음 예제 확인


예제)

#include <iostream>
#include <string>
using namespace std;

int main(void)
{
  string str1="I like ";
  string str2="string class";
  string str3=str1+str2;

  cout<<str1<<endl;
  cout<<str2<<endl;
  cout<<str3<<endl;

  str1+=str2;
  if(str1==str3)   
    cout<<"동일 문자열!"<<endl;
  else
    cout<<"동일하지 않은 문자열!"<<endl;

  string str4;
  cout<<"문자열 입력: ";
  cin>>str4;
  cout<<"입력한 문자열: "<<str4<<endl;
  return 0;
}

결과


위의 결과를 보면 알수 있듯이 string class를 사용하면

객체끼지 덧셈으로 문자열을 합칠 수도 있다.




이런 class가 어떻게 구현되는지 직접 구현해 보기로 한다.


예제)

#include <iostream>
#include <cstring>

using namespace std;

class String
{
  private:
    int len;
    char * str;

  public:
    String();
    String(const char * s);
    String(const String& s);
    ~String();
    String& operator=(const String& s);
    String& operator+=(const String& s);
    bool operator==(const String& s);
    String operator+(const String& s);

    friend ostream& operator<<(ostream& os, const String& s);
    friend istream& operator>>(ostream& os, const String& s);
};

String::String()
{
  len=0;
  str=NULL;
}

String::String(const char* s)
{
  len=strlen(s)+1;
  str=new char[len];
  strcpy(str, s);
}

String::String(const String& s)
{
  len=s.len;
  str=new char[len];
  strcpy(str, s.str);
}

String::~String() 
{
  if(str!=NULL)
    delete []str; 
}

String& String::operator= (const String& s)
{
  if(str!=NULL)
    delete []str;
  len=s.len;
  str=new char[len];
  strcpy(str, s.str);
  return *this;
}

String& String::operator+= (const String& s)
{
  len+=(s.len-1);
  char* tempstr=new char[len];
  strcpy(tempstr, str);
  strcat(tempstr, s.str);

  if(str!=NULL)
    delete []str;
  str=tempstr;
  return *this;
}

bool String::operator== (const String& s)
{
  return strcmp(str, s.str) ? false : true;
}

String String::operator+ (const String& s)
{
  char* tempstr=new char[len+s.len-1];
  strcpy(tempstr, str);
  strcat(tempstr, s.str);
  
  String temp(tempstr);
  delete []tempstr;
  return temp;
}

ostream& operator<< (ostream& os, const String& s)
{
  os<<s.str;
  return os;
}

istream& operator>> (istream& is, String& s)
{
  char str[100];
  is>>str;
  s=String(str);
  return is;
}

int main()
{
  String str1 = "I like ";
  String str2 = "string class";
  String str3 = str1 + str2;

  cout << str1 << endl;
  cout << str2 << endl;
  cout << str3 << endl;

  str1 += str2;
  if(str1 == str3)
  {
    cout << "동일한 문자열" << endl;
  }
  else
  {
    cout << "다른 문자열" << endl;
  }

  String str4;
  cout << "문자열 입력 : ";
  cin >> str4;
  cout << "입력한 문자열 : " << str4 << endl;

  return 0;
}

결과



이렇게 구현할 수 있다.










------- Template (템플릿)


모형자라는 뜻이 담겨있는데 어떤 틀을 만드는 것이다.

아래 예제를 참고


예제)

#include <iostream>
#include <string>

using namespace std;

template <typename T>
T Add(T num1, T num2)
{
  return num1+num2;
}

int main()
{
  cout << Add<int>(1520<< endl;
  cout << Add<double>(0.51.0<< endl;
  cout << Add<int>(3.12.2<< endl;
  cout << Add<double>(5.410.15<< endl;
  cout << Add<string>("Hi"" world"<< endl;

  return 0;
}

결과


template <typename "타입 이름">

이 타입을 적용할 변수 앞에 "타입 이름"을 적어서 사용한다.


이런식으로 다른 타입의 오버로딩 함수를 정의하지 않아도 짧게 코드를 작성할 수 있다.

컴파일 과정 중에 다른 타입의 함수를 컴파일러가 코드를 작성하여 넣어주기 때문에

프로그램의 크기가 더 작아지는 것은 아니다.





함수와 인수 사이에 <타입>을 넣어야 하는데, 만약 넣지 않으면 묵시적 변환이 일어난다.

다음 예제 확인


예제)

#include <iostream>
#include <string>

using namespace std;

template <typename T>
T Add(T num1, T num2)
{
  return num1+num2;
}

int main()
{
  cout << Add(1520<< endl;
  cout << Add(0.51.0<< endl;
  cout << Add(3.12.2<< endl;
  cout << Add(5.410.15<< endl;

  //cout << Add("Hi", " world") << endl;
  string obj1("Hi");
  string obj2(" world");
  cout << Add(obj1, obj2) << endl;

  return 0;
}

결과


위에서 보듯이 <타입>을 생략하면 뒤에 오는 타입에 맞게 묵시적으로 변환되기 때문에

의도치 않은 변환이 일어날 수도 있다.




만약 오버로딩 함수와 템플릿 함수를 함께 정의해 놓았을 경우는 어떻게 될까?

아래 예제 참고

예제)

#include <iostream>

using namespace std;

template <typename T>
T Add(T num1, T num2)
{
  cout << "T Add(T num1, T num2)" << endl;
  return num1 + num2;
}

int Add(int num1, int num2)
{
  cout << "Add(int num1, int num2)" << endl;
  return num1 + num2;
}

double Add(double num1, double num2)
{
  cout << "Add(double num1, double num2)" << endl;
  return num1 + num2;
}

int main()
{
  cout << Add(57<< endl;
  cout << Add(3.77.5<< endl;
  cout << Add<int>(57<< endl;
  cout << Add<double>(3.77.5<< endl;

  return 0;
}

결과


이렇듯 겹치게 되면 확실하게 해야한다.








설정

트랙백

댓글

20141029 (C++ ()연산자와 Functor(펑터), ARM SAM-BA)

168일차




--------

C++

--------



------- 연산자 ()와 Functor(펑터)


객체를 함수처럼 사용하는 것을 말한다.

다음 예제로 확인


예제)

#include <iostream>


using namespace std;


class Point

{

private:

int xpos;

int ypos;


public:

Point(int x = 0, int y = 0) : xpos(x), ypos(y)

{ }


Point operator+(const Point& pos) const

{

return Point(xpos + pos.xpos, ypos + pos.ypos);

}


friend ostream& operator<<(ostream& os, const Point& pos);

};


ostream& operator<<(ostream& os, const Point& pos)

{

cout << "[" << pos.xpos << ", " << pos.ypos << "]" << endl;

return os;

}


class Adder

{

public:

int operator()(const int& n1, const int& n2)

{

return n1 + n2;

}


double operator()(const double& n1, const double& n2)

{

return n1 + n2;

}


Point operator()(const Point& n1, const Point& n2)

{

return n1 + n2;

}

};


int main()

{

Adder adder;


cout << adder(1, 3) << endl;

cout << adder(1.5, 1.7) << endl;

cout << adder(Point(3,4), Point(7, 7));


return 0;

}




객체를 함수처럼 사용하는 것을 Functor라고 부른다.

또 다른 예제


예제)

#include <iostream>


using namespace std;


class SortRule              // 추상 클래스

{

public:

virtual bool operator()(int num1, int num2) const = 0;        // 순수 가상함수

};


class AscendingSort : public SortRule

{

public:

bool operator()(int num1, int num2) const

{

if(num1 > num2)

return true;

else

return false;

}

};


class DescendingSort : public SortRule

{

public:

bool operator()(int num1, int num2) const

{

if(num1 < num2)

return true;

else

return false;

}

};


class DataStorage

{

private:

int * arr;

int idx;

const int MAX_LEN;


public:

DataStorage(int arrlen) : idx(0), MAX_LEN(arrlen)

{

arr = new int[MAX_LEN];

}


void AddData(int num)

{

if(MAX_LEN <= idx)

{

cout << "더 이상 저장이 불가능합니다." << endl;

return;

}

arr[idx++] = num;

}


void ShowAllData()

{

for(int i= 0; i < idx; ++i)

{

cout << arr[i] << " ";

}

cout << endl;

}


void SortData(const SortRule& functor)

{

for(int i = 0; i < (idx - 1); ++i)

{

for(int j = 0; j < (idx - 1); ++j)

{

if(functor(arr[j], arr[j+1]))

{

int temp = arr[j];

arr[j] = arr[j + 1];

arr[j + 1] = temp;

}

}

}

}

};


int main()

{

DataStorage storage(5);


storage.AddData(40);

storage.AddData(30);

storage.AddData(50);

storage.AddData(20);

storage.AddData(10);


storage.SortData(AscendingSort());

storage.ShowAllData();


storage.SortData(DescendingSort());

storage.ShowAllData();


return 0;

}

결과



이렇게 사용한다.




















-------------

ARM

-------------



------- SAM-BA


ARM에 프로그램을 올려줄 때 SAM-BA를 사용한다.

현재 실습용 장비인데,

항상 프로그램을 넣어주기 전에 TST를 켜고 POWER을 켜서

안에 프로그램을 지워주고, PC와 연결하여 프로그램을 넣었다.


그 구동 원리는


(Memory)


칩 내부에 SAM-BA 프로그램이 저장되어 있어,

TST를 켜면 SAM-BA 프로그램이 Flash에 저장되고,

Windows와 통신이 가능하게 되면서 프로그램을 넣을 수 있게 되는 것이었다.



설정

트랙백

댓글

20141028 (C++ new, delete, *, -> 연산자 오버로딩, ARM 소스변환)

167일차






------------

C++

------------



------- 생성자


- 다음 예제 확인


소스)

class AAA

{

private:

int num;

public:

AAA(int n=0): num(n)

{  

cout<<"AAA(int n=0) : " << num <<endl;

}

};


int main(void)

{

AAA obj1(12);

// AAA buff(); // error, 인수가 있는 생성자를 호출하는데 아무 값도 안넣어서 warning.

AAA buff; // ok, 기본 생성자를 호출하였는데, 값이 없으니 기본으로 num에 0이 들어간다.

AAA * temp = new AAA;


delete temp;


return 0;

}

결과




주석 처리된 부분을 컴파일하면 아래와 같은 warning이 뜬다.

warning C4930: 'AAA buff(void)': 프로토타입 함수가 호출되지 않았습니다. 변수 정의로 사용하려고 한 것은 아닌지 확인하십시오.






- 또 비슷한 다른 예제

소스)
class AAA
{
private:
int num;

public:
AAA(int n=0): num(n)
{  
cout << "AAA(int n=0) : " << num <<endl;
}

AAA(const AAA &ref): num(ref.num)
{  
cout << "AAA(const AAA & ref)" << endl;
}

AAA & operator=(const AAA &ref)
{
num = ref.num;
cout << "operator=(const AAA &ref) : " << num <<endl;
return *this;
}

~AAA()
{
cout << "bye~ : " << num << endl;
}

void change()
{
num = 100;
cout << "num = " << num << endl;
}
};

int main(void)
{
AAA obj1(12); // num에 12 값인 obj1 생성

obj1 = 10; // 연산자= 호출됨, num에 10 값인 임시 객체가 생성되어 obj1에 값이 복사된다.
obj1.change(); // 임시 객체와 obj1을 분간하기 위해 넣음.

return 0;
}
결과











------- 어제한 소스

- 이 경우는 new로 heap에 할당 받은 경우.
BoundCheckPointPtrArray arr(3);
arr[0] = new Point(3, 4);
arr[1] = new Point(5, 6);
arr[2] = new Point(7, 8);

- 이 경우는 stack에 할당 받은 경우.
BoundCheckPointArray arr(3);
arr[0] = Point(3, 4);
arr[1] = Point(5, 6);
arr[2] = Point(7, 8);

둘 중에 heap에 할당 받는 것이 더 빠르다.
stack에 할당 받을 경우, 임시객체가 발생해서 할당 받고, 복사해 주기 때문에 더 느려지고,
heap에 할당 받으면, 할당 받고, 주소만 받기 때문에 더 효율적이다.













------- new, delete 연산자 오버로딩


new, delete 연산자도 오버로딩이 가능하다.


우선 new 연산자가 하는 일은 다음과 같다.

1. 메모리 공간의 할당

2. 생성자의 호출

3. 할당하고자 하는 자료형에 맞게 반환된 주소 값의 형 변환


객체가 아직 만들어지지 않았는데, 이 연산자가 호출이 된다는 것은

static으로 선언됐다.


예제)

class Point

{

private:

int xpos;

int ypos;


public:

Point(int x = 0, int y = 0) : xpos(x), ypos(y)

{

cout << "Point()" << endl;

}


friend ostream& operator<<(ostream& os, const Point& pos);


void * operator new (size_t size) //new 연산자 오버로딩

{

cout << "operator new : " << size << endl;

void * adr = new char[size];

return adr;

}


void operator delete (void * adr) //delete 연산자 오버로딩

{

cout << "operator delete ()" << endl;

delete []adr;

}

};


ostream& operator<<(ostream& os, const Point& pos)

{

os << "[" << pos.xpos << ", " << pos.ypos << "]" << endl;

return os;

}


int main()

{

Point * ptr = new Point(3, 4);

cout << *ptr;

delete ptr;

return 0;

};

결과


new 연산자가 실행되는 순서와 결과에 나오는 순서가 같다.

1. 메모리 할당

2. 생성자

3. 주소값 변환


이렇게 new, delete도 연산자 오버로딩이 가능하다.

또 다음과 같이 오버로딩이 가능하다.


예제)

class Point

{

private:

int xpos;

int ypos;


public:

Point(int x = 0, int y = 0) : xpos(x), ypos(y)

{

cout << "Point()" << endl;

}


friend ostream& operator<<(ostream& os, const Point& pos);


void operator new (size_t size) //new 연산자 오버로딩

{

cout << "operator new : " << size << endl;

void * adr = new char[size];

return adr;

}


void operator new[] (size_t size) //new[] 연산자 오버로딩

{

cout << "operator new[] : " << size << endl;

void * adr = new char[size];

return adr;

}


void operator delete (void * adr) //delete 연산자 오버로딩

{

cout << "operator delete ()" << endl;

delete []adr;

}


void operator delete[] (void * adr) //delete[] 연산자 오버로딩

{

cout << "operator delete[] ()" << endl;

delete []adr;

}

};


ostream& operator<<(ostream& os, const Point& pos)

{

os << "[" << pos.xpos << ", " << pos.ypos << "]" << endl;

return os;

}


int main()

{

Point * ptr = new Point(3, 4);

cout << *ptr;

delete ptr;

return 0;

};

결과



이렇게 new, delete 연산자도 오버로딩이 가능하다.









------- *, -> 연산자 오버로딩


*, ->도 오버로딩이 가능하다.

다음 예제 확인


예제)

class Number

{

private:

int num;


public:

Number(int n) : num(n) { }

void ShowData()

{

cout << "ShowData : " << num << endl;

}


Number * operator->()

{

cout << "->" << endl;

return this;

}


Number& operator*()

{

cout << "*" << endl;

return *this;

}

};


int main()

{

Number num(20);

num.ShowData();


(*num) = 30;

num->ShowData();

(*num).ShowData();


cout << "********************" << endl;


( num.operator*() ).ShowData(); // (*num).ShowData()이 이렇게 해석된다.

num.operator->()->ShowData(); // num->ShowData()이 이렇게 해석된다.


return 0;

}

결과











--------------

ARM

--------------



------- 소스 변환


예제)

//#include "myAT91SAM7S256.h"

#include "AT91SAM7S256.h"


int main()

{

//PMC_PCER = (1<<2);


//PIO_PDR = 0xFFFFFFFF;

//PIO_PER = PA0|PA1; // Port Enable PA0~1

//PIO_OER = PA1; // PA1 is Output

//PIO_ODR = PA0; // PA0 is Input


//-> 위에 소스를 아래처럼 변환

AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_PIOA);

AT91C_BASE_PIOA->PIO_PDR = 0xFFFFFFFF;

AT91C_BASE_PIOA->PIO_PER = AT91C_PIO_PA0 | AT91C_PIO_PA1;

AT91C_BASE_PIOA->PIO_OER = AT91C_PIO_PA1;

AT91C_BASE_PIOA->PIO_ODR = AT91C_PIO_PA0;



while (1)

{


// if PA0 were Hi

if(AT91C_PIO_PA0 == (AT91C_BASE_PIOA->PIO_PDSR & AT91C_PIO_PA0 ))

{

// then PA1 out is Hi

AT91C_BASE_PIOA->PIO_SODR = AT91C_PIO_PA1;

}

// if PA0 were Lo

else 

{

// then PA1 out is Lo

AT91C_BASE_PIOA->PIO_CODR = AT91C_PIO_PA1;

}


}

return 0;

}








설정

트랙백

댓글

20141027 (C++ 객체 초기화, 대입연산자, 배열 연산자, ARM ADS)

166일차










--------------

C++

--------------







------- 객체 초기화


객체를 초기화하는 방법이 두 가지 있습니다.

다음 예제 확인


예제)

//ImproveInit.cpp

#include <iostream>


using namespace std;


class AAA

{

private:

int num;


public:

AAA(int n = 0) : num(n) // default 생성자

{

cout << "AAA(int n = 0)" << endl;

}


AAA(const AAA& ref) : num(ref.num) // 받은 인자로 초기화하는 생성자

{

cout << "AAA(const AAA& ref)" << endl;

}


AAA& operator=(const AAA& ref) // 대입 연산자

{

num = ref.num;

cout << "operator=(const AAA& ref)" << endl;

return *this;

}

};


class BBB

{

private:

AAA mem;


public:

BBB(const AAA& ref) : mem(ref) // 생성자로 초기화

{

}

};


class CCC

{

private:

AAA mem;


public:

CCC(const AAA& ref)

{

mem = ref; // 대입 연산자로 초기화

}

};


int main()

{

AAA obj1(12);

cout << "*************************" << endl;


BBB obj2(obj1);

cout << "*************************" << endl;


CCC obj3(obj1);

cout << "*************************" << endl;


return 0;

}

결과



위의 결과 화면에서 확인할 수 있듯이,

생성자로 초기화한 것과, 대입 연산자로 초시화한 것이

속도 차이가 분명하다.


생성자로 초기화한 것은 한 번(생성자만 호출),

대입 연산자로 초기화한 것은 두 번(생성자, 대입 연산자 호출)

실행한다.











------- 대입 연산자, const


다음 예제를 보고 하겠습니다.


예제)

#include <iostream>

#include <cstdlib>


using namespace std;


class BoundCheckIntArray

{

private:

int * arr;

int arrlen;


BoundCheckIntArray(const BoundCheckIntArray& arr)

{ }

BoundCheckIntArray& operator=(const BoundCheckIntArray& ref)

{ }

public:

BoundCheckIntArray(int len) : arrlen(len) // 생성자

{

arr = new int[len];

}


int& operator[] (int idx) // 배열 연산자

{

if(idx < 0 || idx >= arrlen)

{

cout << "Array index out of bound exception" << endl;

exit(1);

}

return arr[idx];

}

/*

int operator[] (int idx) const // 배열 연산자 (const, 오버로딩)

{

if(idx < 0 || idx >= arrlen)

{

cout << "Array index out of bound exception" << endl;

exit(1);

}

return arr[idx];

}

*/

int GetArrLen() const

{

return arrlen;

}


~BoundCheckIntArray()

{

delete []arr;

}

};


void ShowAllData(const BoundCheckIntArray& ref)

{

int len = ref.GetArrLen();

for(int idx = 00; idx < len; ++idx)

{

cout << ref[idx] << endl; // 배열 연산자  호출

}

}


int main()

{

BoundCheckIntArray arr(5);


for(int i = 0; i<5; ++i)

{

arr[i] = (i+1) * 11;

}


ShowAllData(arr);


return 0;

}


이 예제는 컴파일 시, 에러입니다.

이유는 ShowAllData 함수에서 배열 연산자 호출하는 부분이 있는데,

인수로 받는 ref를 보시면 const로 선언이 된 것을 확인할 수 있습니다.

따라서 이 함수에서 ref 관련 호출할 수 있는 함수는 const로 선언되어야만

호출 가능합니다.


따라서 위에 주석을 제거하면 operator= 이 const로 오버로딩되어

정상적으로 동작하게 됩니다.














------- 배열 연산자


아래 예제를 봅시다.


예제)

#include <iostream>

#include <cstdlib>


using namespace std;


class Point

{

private:

int xpos;

int ypos;


public:

Point(int x = 0, int y = 0) : xpos(x), ypos(y)

{ }

friend ostream& operator<<(ostream& os, const Point& pos); // << 연산자 friend

};


ostream& operator<<(ostream& os, const Point& pos) // << 연산자 정의

{

os << '[' << pos.xpos << ", " << pos.ypos << "]" << endl;

return os;

}



class BoundCheckPointArray

{

private:

Point * arr;

int arrlen;


BoundCheckPointArray(const BoundCheckPointArray& arr)

{ }

BoundCheckPointArray& operator=(const BoundCheckPointArray& arr)

{ }


public:

BoundCheckPointArray(int len) : arrlen(len) // 생성자

{

arr = new Point[len]; // 멤버 변수 arr 메모리 할당

}


Point& operator[](int idx) // 배열 연산자

{

if(idx < 0 || idx >= arrlen)

{

cout << "Array index out of bound exception" << endl;

exit(1);

}

return arr[idx];

}


Point operator[] (int idx) const // 배열 연산자

{

if(idx < 0 || idx >= arrlen)

{

cout << "Array index out of bound exception" << endl;

exit(1);

}

return arr[idx];

}


int GetArrLen() const

{

return arrlen;

}

~BoundCheckPointArray()

{

delete []arr;

}

};


int main()

{

BoundCheckPointArray arr(3);


arr[0] = Point(3, 4);

arr[1] = Point(5, 6);

arr[2] = Point(7, 8);


for(int i = 0; i < arr.GetArrLen(); ++i)

{

cout << arr[i];

}


return 0;

}

결과



이 소스에서

arr[0] = Point(3, 4);

명령이  배열연산자에 의해 실행되는 과정을 보면


1. Point 객체 생성 (생성자에 의해 객체 멤버 값은 xpos = 3, ypos = 4)


Point(int x = 0, int y = 0) : xpos(x), ypos(y)



2. arr[0]   ==  arr.operator[](0)   그래서 []함수? 호출


Point& operator[](int idx) // 배열 연산자

{

if(idx < 0 || idx >= arrlen)

{

cout << "Array index out of bound exception" << endl;

exit(1);

}

return arr[idx];

}



3. return 값이 Point& 형이므로 결국 arr.arr[0] 변수를 가리키므로

arr.arr[0]에 값이 대입된다.















---------------

ARM

---------------




------- ARM ADS


ARM Developer Suite 라는 줄임말로 ARM 사에서 제작한 유료 C컴파일러

현재는 단종되었다고 합니다.




---- 설치법


- SETUP.EXE를 실행
















- 설치가 끝나면 아래 라이센스 관련 창이 뜹니다.







- 설치 파일이 있던 곳에 가면 CRACK이란 폴더에 위치하고 있습니다.






- 다음과 같은 결과가 보이면 다음으로 갑니다.








- 설치가 완료되면 바탕화면에 바로가기가 생성되지 않기 때문에

C:\Program Files\ARM\ADSv1.2\Bin 으로 가서 IDE.exe 바로가기를 만드셔서 사용하시거나

시작 -> Programs -> ARM Developer Suite v1.2 -> CodeWarrior for ARM Developer Suite 실행하면 됩니다.




- cmd 창에서 버전 확인


ARM 사에서는 문제점들을 보완한 패치버전을 계속해서 내어놓고 있었다고 합니다.

최신 버전으로 업데이트하는 방법을 알아볼 예정인데요

http://www.arm.com/support/downloads/info/4554.html 에서 받을 수 있다는데

현재는 지원이 끝났나 봅니다.


그런데 구하기 쉽더군요ㅋㅋㅋㅋ

4554_ADS12_848_Windows.exe




- 아래와 같이 실행



- 버전이 상승된 걸 확인할 수 있습니다.








---- 실행법


- 먼저 실행하기 전에 예제 파일을 받아옵니다.

http://cpu.kongju.ac.kr/frame3.htm 에 가셔서

책 관련 자료실 -> 166번 게시글 (ADS v1.2용의 C언어 예제 파일)

예제 파일을 받아 ADS 가 설치 된 곳에 함께 둡니다.




- IDE.exe 나 시작프로그램에 CodeWarrior... 실행










- ADS v1.2용 C언어 예제 프로그램에서 항상 공통적으로 사용하는 스타트업 파일이 3개 있는데, 이것들을

하나의 그룹으로 묶어서 등록하여 두면 편리하므로 그룹을 생성합니다.







- Cstartup.s 열기





- 나머지 두 파일도 추가








- 이제 실행할 예제 소스를 추가합니다.



- Xtest01_1.c


소스 내용은


#include "AT91SAM7S256.h"

#include "lib_AT91SAM7S256.h"

#include "OK7S256ads.h"


int main(void)

{

  MCU_initialize(); // initialize AT91SAM7S256 & kit


  while(1)

    { Beep(); // beep 


      LED_on(LED1); // LED1 on 

      Delay_ms(1000);

      LED_off(LED1); // LED1 off

      Delay_ms(1000);

      

      LED_on(LED2); // LED2 on 

      Delay_ms(1000);

      LED_off(LED2); // LED2 off

      Delay_ms(1000);

      

      LED_on(LED2|LED1); // LED2 and LED1 on 

      Delay_ms(1000);

      LED_off(LED2|LED1); // LED2 and LED1 off

      Delay_ms(1000);

    }

}

로 LED 2개를 껐다 켰다 합니다.



- 여기까지 새로운 프로젝트 Test.mcp가 만들어지고 여기에 3개의 스타트업 파일과 사용자 프로그램을 등록 완료하였습니다.



- Tip> 프로젝트에서는 타겟을 기본적으로 DebugRel 폴더로 지정한다. Debug 폴더로 하면 최종 출력 파일에

          디버그 정보를 포함하는데 주력하며 최적화 기능은 희생되는데 비해 Release 폴더로 하면 반대로 최

          적화 기능에 주력하고 디버그 정보는 포함하지 않는다. 그러나, DebugRel 폴더로 지정하면 이것들의

          중간 정도로 처리된다.



- 새로운 프로젝트의 환경 설정

    프로젝트의 프로그래밍 환경설정을 시작하려면 프로젝트 창에서 DebugRel Settings.. 아이콘을 누르거나

  Edit탭의 DebugRel Settings... 메뉴를 선택하거나 Alt+F7단축키를 이용합니다.











- 위에 혹시나 잘 안보이는 글자는 " 0x00000000 ", " 0x00200000 " 입니다.










- 에디터를 사용할 때, 탭 간격 조절






사용자 프로그램을 더블클릭하게 되면 파일 내용을 볼 수 있습니다.

- 컴파일은 프로젝트 창에서 사용자 프로그램을 오른쪽 마우스 클릭하고 Compile을 누르고,

- 컴파일 및 링크를 수행하려면 프로젝트 창에서 Make 아이콘을 누르거나 메인화면에 있는 Make아이콘을

   눌러도 되며, Project탭에서 Make메뉴를 선택하거나 F7단축키를 이용하는 방법이 있습니다.




- 컴파일시 entry 가 없다고 warning?이 뜨던데, 삼바로 arm에 넣으면 잘 돌아갑니다ㅋㅋㅋㅋ

bin 파일 위치는 프로젝트 이름을 test로 했으니, 내문서 -> test -> test_Data -> DebugRel 에 있습니다.



출처 : http://alisa2304.tistory.com/35








------- Cstartup.s 소스 분석



;-------------------------------- ; <- 주석입니다.

;- ARM Core Mode and Status Bits

;--------------------------------

IRQ_STACK_SIZE EQU (3*8*4) ; 2 words per interrupt priority level

;요기는 #define과 같은 맥락으로

;IRQ_STACK_SIZE 는 (3*8*4)로 치환한다는 뜻이겠죠?


ARM_MODE_USER EQU 0x10

ARM_MODE_FIQ EQU 0x11

ARM_MODE_IRQ EQU 0x12

ARM_MODE_SVC EQU 0x13

ARM_MODE_ABORT EQU 0x17

ARM_MODE_UNDEF EQU 0x1B

ARM_MODE_SYS EQU 0x1F


I_BIT EQU 0x80

F_BIT EQU 0x40

T_BIT EQU 0x20


;------------------------------------------------------------------------------

;- Area Definition

;------------------------------------------------------------------------------

AREA  reset, CODE, READONLY ; 읽기전용, CODE 영역

ENTRY

EXPORT entry ; entry를 외부에 보이게.


...


entry

B InitReset ; 0x00 Reset handler

; B 는 jmp 와 같은 명령으로 InitReset으로 가볼까요?

undefvec

B undefvec ; 0x04 Undefined Instruction

...



;--------------------

;- The reset handler

;--------------------

InitReset ; 여기까지 날아왔습니다~

...

IMPORT AT91F_LowLevelInit ; 외부에서 저 함수를 가져온다는 명령.


;- minimum  C initialization

ldr r13,=0x00204000 ; temporary stack in internal RAM

; ldr은 LoaD Register라는 명령으로 r13 Reg 에 0x00204000 값을 넣습니다.

ldr r0,=AT91F_LowLevelInit

; r0 Reg 에 함수 주소?를 넣었어요

mov lr, pc

bx r0

; bx 는 실행, 분기 기능으로 r0에 있는 것을 실행한다는 뜻이니 AT91F_LowLevelInit 을 실행합니다.




- AT91F_LowLevelInit은 Cstartup_SAM7.c 에 정의되어 있습니다.


void AT91F_LowLevelInit(void)

{

  int i;

  AT91PS_PMC pPMC = AT91C_BASE_PMC;

...





오늘은 여기까지... 얼마 남지 않은 시간. 무어싰게 열심히 마무리 합시다!!!

설정

트랙백

댓글

20141015 (C++ class 내에 const static, mutable, 상속, )

158일차







-----------

C++

-----------




------- const static 멤버


class 내에 선언된 const static 멤버는 class 내에서 초기화를 한다.


예제)

#include <iostream>


using namespace std;


class CountryArea

{

public:

const static int RUSSIA = 1707540; // class 내에서 초기화

const static int CANADA = 998467; // class 내에서 초기화

const static int CHINA = 957290; // class 내에서 초기화

const static int SOUTH_KOREA = 9922; // class 내에서 초기화

};


int main(void)

{

cout << "러시아 면적: " << CountryArea::RUSSIA << "㎢" << endl;

cout << "캐나다 면적: " << CountryArea::CANADA << "㎢" << endl;

cout << "중국 면적: " << CountryArea::CHINA << "㎢" << endl;

cout << "한국 면적: " << CountryArea::SOUTH_KOREA << "㎢" << endl;


return 0;

}









------- mutable


const 함수 내에서 예외적으로 값 변경을 허용하는 키워드


예제)

#include <iostream>


using namespace std;


class SoSimple

{

private:

int num1;

mutable int num2;


public:

SoSimple(int n1, int n2) : num1(n1), num2(n2)

{

}


void ShowSimpleData() const

{

cout << num1 << ", " << num2 << endl;

}


void CopyToNum2() const

{

num2 = num1; // const 변수인데 값 수정 가능

}

};


int main()

{

SoSimple sm(1, 2);


sm.ShowSimpleData();

sm.CopyToNum2();

sm.ShowSimpleData();


return 0;

}



좋은 기능이지만 mutable을 사용하지 않는 편이 좋다









------- 상속


Ray 라는 class와 Tico라는 class를 만드는데,

바퀴, 엔진, 색상... 같은 변수는 공통적으로 들어간다.

비슷한 class를 만들 때마다 변수 목록을 복사 + 붙여넣기 하면 되지만,

번거롭다.....


그래서 상속이라는 것이 있는데, 공통적으로 반드시 들어가는 변수, 함수를 모아

Car 라는 class로 정의 하고 Ray, Tico 같은 class에서 상속만 받으면 된다.


상속 받는 법은 아래 예제를 참고한다.


예제)

#include <iostream>

#include <cstring>


using namespace std;


class Car

{

public: // 생략시 private

int Tire;

int Eng;

int Handle;

char Color[100];


public:

void Print()

{

cout << "Tire\t: " << Tire << endl;

cout << "Color\t: " << Color << endl;

}

};

// Car class 가 결함이 있다면

// 상속 받는 다른 class도 다 결함을 갖는다


class Ray : public Car // 상속 받음

{

};


class Tico : public Car // 상속 받음

{

};


int main()

{

Ray MyCar1;

Tico MyCar2;


MyCar1.Tire = 4; // 상속을 받으면 상위 class의 멤버

MyCar2.Tire = 4; // 자신의 멤버처럼 사용할 수 있다.


strcpy(MyCar1.Color, "PINK"); // 상속을 받으면 상위 class의 멤버

strcpy(MyCar2.Color, "RED"); // 자신의 멤버처럼 사용할 수 있다.


MyCar1.Print(); // 상속을 받으면 상위 class의 멤버

MyCar2.Print(); // 자신의 멤버처럼 사용할 수 있다.


return 0;

}





공통적인 클래스를 부모 클래스라 부르고, 

부모 클래스의 멤버를 상속받는 클래스를 자식 클래스라고 한다.


부모, 자식 말고도 여러가지로 불리는데 아래와 같다.



아주 편리한 기능이지만 단점도 있다.

상위 클래스에 결함이 있다면 하위 클래스도 똑같이 물려 받게 된다.






























설정

트랙백

댓글

20141014 (C++ class내에 const, friend, static, 시리얼 통신 winapi)

157일차








-----------

C++

-----------



------- const

예제를 통하여 class 내에 const의 기능을 더 확인한다.


예제1)

#include <iostream>


using namespace std;


class SoSimple

{

private:

int num;

public:

SoSimple(int n) : num(n)

{

}


SoSimple & AddNum(int n)

{

num = num + n;

return *this;

}


void ShowData() const

{

cout << "num : " << num << endl;

}

};


int main()

{

const SoSimple obj(7);


// obj.AddNum(20); // error!

obj.ShowData(); // ok!


return 0;

}



obj 객체가 const 로 선언되었기 때문에, AddNum 함수가 const 함수가 아니라서

호출 자체가 에러인 것이다.




예제2)

#include <iostream>


using namespace std;


class SoSimple

{

private:

int num;

public:

SoSimple(int n) : num(n)

{

}


SoSimple & AddNum(int n)

{

num = num + n;

return *this;

}


void SimpleFunc()

{

cout << "SimpleFunc: " << num << endl;

}


void SimpleFunc() const

{

cout << "const SimpleFunc: " << num << endl;

}

};


void YourFunc(const SoSimple & obj) // 인자를 const로 받음

{

obj.SimpleFunc();

}


int main()

{

SoSimple obj1(2);

const SoSimple obj2(7);


obj1.SimpleFunc();

obj2.SimpleFunc();


YourFunc(obj1);

YourFunc(obj2);


return 0;

}


결과



YourFunc(const SoSimple & obj) 함수의 인수 자체가 const로 받았기 때문에

그 안에서 호출되는 객체는 모두 const로 호출된다.










------- friend

class 내에 friend를 선언하면 선언된 class에 private 멤버에 접근이 가능하다.


예제)

#include <iostream>

#include <cstring>


using namespace std;


class Girl; // class도 선언만 가능


class Boy

{

private:

int height;

friend class Girl; // friend 선언 위치는 class 내에 어느 곳이든 가능


public:

Boy(int len) : height(len)

{

}


void ShowYourFriendInfo(Girl & frn); // class 내에서 선언만 해두고 정의는 더 밑에 있다

};


class Girl

{

private:

char phNum[20];


public:

Girl(char * num)

{

strcpy(phNum, num);

}


void ShowYourFriendInfo(Boy & frn); // class 내에서 선언만 해두고 정의는 더 밑에 있다

friend class Boy; // friend 선언 위치는 class 내에 어느 곳이든 가능

};


// 정의가 여기 있는 이유는 Boy class에서 Girl class의 정의가

// 어떤지 모르니 Boy class 내에서 Girl class 에 대한 언급을 할 수가 없기에

// 밑에 다 정의를 해둔 것이다

void Boy::ShowYourFriendInfo(Girl & frn)

{

cout << "Her phone number: " << frn.phNum << endl; // private 멤버에 접근 가능

}


void Girl::ShowYourFriendInfo(Boy & frn)

{

cout << "His height: " << frn.height << endl; // private 멤버에 접근 가능

}


int main()

{

Boy boy(170);

Girl girl("010-1234-5678");


boy.ShowYourFriendInfo(girl);

girl.ShowYourFriendInfo(boy);


return 0;

}





여기서 함수의 접근도 설정할 수 있는데


int main()

{

Boy boy(170);

Girl girl("010-1234-5678");


boy.ShowYourFriendInfo(girl);

girl.ShowYourFriendInfo(boy);


boy.height = 180; // error!


return 0;

}



boy.height = 180; 코드는 에러인데


class Boy

{

private:

int height;

friend class Girl; // friend 선언 위치는 class 내에 어느 곳이든 가능

friend int main(); // 이렇게 main을 추가함


public:

...


위와 같은 방식으로 main()을 추가하면 main도 boy의 friend가 되었기 때문에

에러가 아니게 된다.










------- static 멤버 변수, 함수

class 내 변수에 static 을 붙이면 그 클래스에 그 변수 한 개만 할당되어

그 클래스의 객체들은 그 한 변수를 공유하게 된다.


class smart

{

public:

static int iNum;

};




또 함수에 static을 붙였을 시,

내부 함수나 변수는 static인 함수와 변수만 사용 가능하다.



예제)

#include <iostream>


using namespace std;


class smart

{

public:

static int iNum;

int iNum2;


public:

static void test()

{

cout << "test()" << endl;

iNum = iNum + 1;

// iNum2 = 0; // error! static 변수가 아니라서 사용 못함

}

};

int smart::iNum = 100;


int main()

{

cout << smart::iNum << endl;

smart::test();

cout << smart::iNum << endl;


return 0;

}



한 가지 흥미로운 것은 객체를 생성하지 않았는데 사용 가능하다는 것이다!!!











-------------------------

시리얼 통신 winapi

-------------------------



------- 어제 작성했던 시리얼 통신 DOS 모드로 작성했던 소스를

그래픽 모드로 재작성 하였다.



DOS 모드 소스)

#include <windows.h>

#include <stdio.h>


DWORD WINAPI Thread_Read(LPVOID);


HANDLE hComm;


int main()

{

u_char caString[10] = "하이~";

//char buf;

DWORD dwWritten;

DCB sPState;

COMMTIMEOUTS cTime;

DWORD ThreadID;

HANDLE hThread;


hComm = CreateFile("COM8", GENERIC_READ | GENERIC_WRITE

, 0, NULL, OPEN_EXISTING

, FILE_ATTRIBUTE_NORMAL, 0);


if(INVALID_HANDLE_VALUE == hComm)

{

printf("포트 열 수 없음\n");

return 0;

}


if(0 == SetupComm(hComm, 4096, 3096))

{

printf("버퍼 설정 에러\n");

CloseHandle(hComm);

return 0;

}


if(0 == PurgeComm(hComm, PURGE_TXABORT | PURGE_TXCLEAR))

{

printf("버퍼 초기화 에러\n");

CloseHandle(hComm);

return 0;

}


sPState.DCBlength = sizeof(sPState);


if(0 == GetCommState(hComm, &sPState))

{

printf("시리얼 상태 읽기 에러\n");

CloseHandle(hComm);

return 0;

}


sPState.BaudRate = CBR_38400;

sPState.ByteSize = 8;

sPState.Parity = EVENPARITY;

sPState.StopBits = ONESTOPBIT;


cTime.ReadIntervalTimeout = MAXDWORD; // 시간 설정, 블록킹 됐을시 기다리는 시간

cTime.ReadTotalTimeoutMultiplier = 0; // 0은 무한 대기

cTime.ReadTotalTimeoutConstant = 0;

cTime.WriteTotalTimeoutMultiplier = 0; // 0은 무한 대기

cTime.WriteTotalTimeoutConstant = 0;


SetCommTimeouts(hComm, &cTime);


if(0 == SetCommState(hComm, &sPState))

{

printf("시리얼 상태 설정 에러\n");

CloseHandle(hComm);

return 0;

}


hThread = CreateThread(NULL, 0, Thread_Read, NULL, 0, &ThreadID);


while(1)

{

caString[0] = getch();

if(0 == WriteFile(hComm, caString, 1, &dwWritten, 0))

{

printf("쓰기 에러\n");

}

else

{

printf("쓰기 성공\n");

}

}


CloseHandle(hComm);


return 0;

}


DWORD WINAPI Thread_Read(LPVOID NotUse)

{

char buf = 0;

DWORD dwRead;


while(1)

{

Sleep(100);

ReadFile(hComm, &buf, 1, &dwRead, NULL);

if(0 != dwRead)

{

printf("[%c] ", buf);

}

}

}






그래픽 모드 소스)

#include <windows.h>

#include "resource.h"


#define ID_EDIT 100


LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

void OpenSPort();

DWORD WINAPI Thread_Read(LPVOID NotUse);

HINSTANCE g_hInst;

LPCTSTR lpszClass=TEXT("Serial");

HWND hMain;


int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance

,LPSTR lpszCmdParam,int nCmdShow)

{

HWND hWnd;

MSG Message;

WNDCLASS WndClass;

g_hInst=hInstance;


WndClass.cbClsExtra=0;

WndClass.cbWndExtra=0;

WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);

WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);

WndClass.hInstance=hInstance;

WndClass.lpfnWndProc=WndProc;

WndClass.lpszClassName=lpszClass;

WndClass.lpszMenuName=MAKEINTRESOURCE(IDR_MENU1);

WndClass.style=CS_HREDRAW | CS_VREDRAW;

RegisterClass(&WndClass);


hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,

NULL,(HMENU)NULL,hInstance,NULL);

ShowWindow(hWnd,nCmdShow);


while (GetMessage(&Message,NULL,0,0)) {

TranslateMessage(&Message);

DispatchMessage(&Message);

}

return (int)Message.wParam;

}


HANDLE hComm;

HANDLE hThread;

BOOL bPortOnOff;

HWND hEdit;


LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

u_char caString[128] = "하이~";

DWORD dwWritten;


switch (iMessage)

{

case WM_CREATE:

hMain = hWnd;

hEdit = CreateWindow(TEXT("edit"), NULL, WS_CHILD | WS_VISIBLE

| WS_BORDER | ES_AUTOHSCROLL

, 10, 10, 100, 25, hWnd, (HMENU)ID_EDIT, g_hInst, NULL);

return 0;

case WM_COMMAND:

switch(LOWORD(wParam))

{

case ID_OPEN:

OpenSPort();

break;

case ID_CLOSE:

if(TRUE == bPortOnOff)

{

CloseHandle(hComm);

CloseHandle(hThread);

bPortOnOff = FALSE;

}

break;

case ID_EDIT:

switch(HIWORD(wParam))

{

}

break;

}

return 0;

case WM_DESTROY:

PostQuitMessage(0);

return 0;

}

return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}


void OpenSPort()

{

static DCB sPState;

static COMMTIMEOUTS cTime;

DWORD ThreadID;


if(TRUE == bPortOnOff)

{

CloseHandle(hComm);

CloseHandle(hThread);

// return 0;

bPortOnOff = FALSE;

}


hComm = CreateFile(TEXT("COM8"), GENERIC_READ | GENERIC_WRITE

, 0, NULL, OPEN_EXISTING

, FILE_ATTRIBUTE_NORMAL, 0);


if(INVALID_HANDLE_VALUE == hComm)

{

MessageBox(hMain, TEXT("포트 열 수 없음"), TEXT("알림"), MB_OK);

return ;

}


if(0 == SetupComm(hComm, 4096, 3096))

{

MessageBox(hMain, TEXT("버퍼 설정 에러"), TEXT("알림"), MB_OK);

CloseHandle(hComm);

return ;

}


if(0 == PurgeComm(hComm, PURGE_TXABORT | PURGE_TXCLEAR))

{

MessageBox(hMain, TEXT("버퍼 초기화 에러"), TEXT("알림"), MB_OK);

CloseHandle(hComm);

return ;

}


sPState.DCBlength = sizeof(sPState);


if(0 == GetCommState(hComm, &sPState))

{

MessageBox(hMain, TEXT("시리얼 상태 읽기 에러"), TEXT("알림"), MB_OK);

CloseHandle(hComm);

return ;

}


sPState.BaudRate = CBR_38400;

sPState.ByteSize = 8;

sPState.Parity = EVENPARITY;

sPState.StopBits = ONESTOPBIT;


cTime.ReadIntervalTimeout = MAXDWORD; // 시간 설정, 블록킹 됐을시 기다리는 시간

cTime.ReadTotalTimeoutMultiplier = 0; // 0은 무한 대기

cTime.ReadTotalTimeoutConstant = 0;

cTime.WriteTotalTimeoutMultiplier = 0; // 0은 무한 대기

cTime.WriteTotalTimeoutConstant = 0;


SetCommTimeouts(hComm, &cTime);


if(0 == SetCommState(hComm, &sPState))

{

MessageBox(hMain, TEXT("시리얼 상태 설정 에러"), TEXT("알림"), MB_OK);

CloseHandle(hComm);

return ;

}


hThread = CreateThread(NULL, 0, Thread_Read, NULL, 0, &ThreadID);


bPortOnOff = TRUE; // 포트 열기가 완료되었으므로 TRUE

}


DWORD WINAPI Thread_Read(LPVOID NotUse)

{

char tt;

static TCHAR buff[] = TEXT("0x00 ");

DWORD dwRead;


while(1)

{

Sleep(50);

ReadFile(hComm, &tt, 1, &dwRead, NULL);

wsprintf(buff, TEXT("0x%02X "), tt);

if(0 != dwRead)

{

SetWindowText(hEdit, buff);

}

}

}









설정

트랙백

댓글

20141013 (C++ 복사 생성자, 시리얼 통신 winapi)

156일차






-----------

C++

-----------



------- 복사 생성자

예제를 보자.


소스)

#include <iostream>

#include <cstring>


using namespace std;


class smart

{

public:

char * p;


public:

smart()

{

cout << "D생성자" << endl;

p = new char[3];

strcpy(p, "Hi");

}


smart(const smart & r) // 복사 생성자, 복사 받는 값은 변경하지 않으니

{ // 보통 앞에 const를 붙인다

p = new char[3];

strcpy(p, r.p);

cout << p << "복사 생성자" << endl;

}


~smart()

{

cout << "~소멸자" << endl;

delete []p;

}


smart test()

{

smart t;

*(t.p) = 'X';

return t;

}

};


int main()

{

smart obj1; // 디폴트 생성자 호출

smart obj2; // 디폴트 생성자 호출


// obj2 = obj1; // 대입 연산자 호출


*(obj2.p) = 'L';


smart obj3 = obj2; // 복사 생성자 호출

smart obj4(obj3); // 복사 생성자 호출


smart obj5 = obj4.test();


cout << "this is test" << endl;


return 0;

}


결과





위 처럼 생성자인데 바로 다른 값을 넣어주는 것을 복사 생성자라 한다.

이것을 선언해 주지 않을시 기본값으로 만들어져 있다.


만약 위 소스에서

smart(const smart & r) // 복사 생성자, 복사 받는 값은 변경하지 않으니

{ // 보통 앞에 const를 붙인다

// p = new char[3];

// strcpy(p, r.p);

cout << p << "복사 생성자" << endl;

}

해버리면 오류다ㅋ


http://wowcat.tistory.com/1946








------- explicit

복사 생성자를 사용할 때 묵시적 변환이 일어나서

복사 생성자가 호출되는 경우가 있는데, 무슨 말이냐 하면...


smart obj1 = obj2;      =>      smart obj1(obj2);


이렇게 묵시적으로 바뀐다.

여기서 smart obj1 = obj2; 형태가 맘에 들지 않으면 복사 생성자의 묵시적 호출을

허용하지 않을 수 있는데, 이때 사용하는 키워드가 explicit 이다.


예제)

class smart

{

public:

smart()

{

cout << "디폴트 생성자" << endl;

}

explicit smart(smart n)

{

cout << "복사 생성자" << endl;

}

};


int main()

{

smart obj1;


smart obj2(obj1);     // ok!

smart obj3 = obj1;    // error!


return 0;

}





------- 임시 객체

실제로 쓰지는 않고 임시 객체를 통해서

생성자와 소멸자가 사라지는 시간을 확인하는 소스이다.


예제)

#include <iostream>


using namespace std;


class Temporary

{

private:

int num;

public:

Temporary(int n) : num(n)

{

cout<<"create obj: "<<num<<endl;

}

~Temporary()

{

cout<<"destroy obj: "<<num<<endl;  

}

void ShowTempInfo()

{

cout<<"My num is "<<num<<endl;

}

};


int main(void)

{

Temporary(100);

cout<<"********** after make!"<<endl<<endl;


Temporary(200).ShowTempInfo();

cout<<"********** after make!"<<endl<<endl;


const Temporary &ref=Temporary(300);

cout<<"********** end of main!"<<endl<<endl;

return 0;

}


결과













---------------------

Winapi 시리얼 통신

---------------------



------- 시리얼 통신

소스를 짜기 앞서,


사용할 포트를 설정해 줘야한다. Virtual Serial Ports Driver라는 프로그램으로

8번과 9번을 연결해 놓았다.


하이퍼 터미널로 9번을 열었다면, 작성할 소스는 8번을 열어야 한다.


소스)

#include <windows.h>

#include <stdio.h>


int main()

{

u_char caString[10] = "하이~";

DWORD dwWritten;

DCB sPState;

HANDLE hComm = CreateFile("COM8", GENERIC_READ | GENERIC_WRITE

, 0, NULL, OPEN_EXISTING

, FILE_ATTRIBUTE_NORMAL, 0);


if(INVALID_HANDLE_VALUE == hComm)

{

printf("포트 열 수 없음\n");

return 0;

}


if(0 == SetupComm(hComm, 4096, 3096))

{

printf("버퍼 설정 에러\n");

CloseHandle(hComm);

return 0;

}


if(0 == PurgeComm(hComm, PURGE_TXABORT | PURGE_TXCLEAR))

{

printf("버퍼 초기화 에러\n");

CloseHandle(hComm);

return 0;

}


sPState.DCBlength = sizeof(sPState);


if(0 == GetCommState(hComm, &sPState))

{

printf("시리얼 상태 읽기 에러\n");

CloseHandle(hComm);

return 0;

}


sPState.BaudRate = CBR_38400;

sPState.ByteSize = 8;

sPState.Parity = EVENPARITY;

sPState.StopBits = ONESTOPBIT;


if(0 == SetCommState(hComm, &sPState))

{

printf("시리얼 상태 설정 에러\n");

CloseHandle(hComm);

return 0;

}


if(0 == ReadFile(hComm, caString, sizeof(caString), &dwWritten, 0))

//if(0 == WriteFile(hComm, caString, sizeof(caString), &dwWritten, 0))

{

printf("쓰기 에러\n");

}

else

{

// printf("쓰기 성공\n");

printf("읽기 성공\n");

printf("[%s]\n", caString);

}


CloseHandle(hComm);


return 0;

}







------- 스레드 + 시리얼 통신

소스)

#include <windows.h>

#include <stdio.h>


DWORD WINAPI Thread_Read(LPVOID);


HANDLE hComm;


int main()

{

u_char caString[10] = "하이~";

//char buf;

DWORD dwWritten;

DCB sPState;

COMMTIMEOUTS cTime;

DWORD ThreadID;

HANDLE hThread;


hComm = CreateFile("COM8", GENERIC_READ | GENERIC_WRITE

, 0, NULL, OPEN_EXISTING

, FILE_ATTRIBUTE_NORMAL, 0);


if(INVALID_HANDLE_VALUE == hComm)

{

printf("포트 열 수 없음\n");

return 0;

}


if(0 == SetupComm(hComm, 4096, 3096))

{

printf("버퍼 설정 에러\n");

CloseHandle(hComm);

return 0;

}


if(0 == PurgeComm(hComm, PURGE_TXABORT | PURGE_TXCLEAR))

{

printf("버퍼 초기화 에러\n");

CloseHandle(hComm);

return 0;

}


sPState.DCBlength = sizeof(sPState);


if(0 == GetCommState(hComm, &sPState))

{

printf("시리얼 상태 읽기 에러\n");

CloseHandle(hComm);

return 0;

}


sPState.BaudRate = CBR_38400;

sPState.ByteSize = 8;

sPState.Parity = EVENPARITY;

sPState.StopBits = ONESTOPBIT;


cTime.ReadIntervalTimeout = MAXDWORD; // 시간 설정, 블록킹 됐을시 기다리는 시간

cTime.ReadTotalTimeoutMultiplier = 0; // 0은 무한 대기

cTime.ReadTotalTimeoutConstant = 0;

cTime.WriteTotalTimeoutMultiplier = 0; // 0은 무한 대기

cTime.WriteTotalTimeoutConstant = 0;


SetCommTimeouts(hComm, &cTime);


if(0 == SetCommState(hComm, &sPState))

{

printf("시리얼 상태 설정 에러\n");

CloseHandle(hComm);

return 0;

}


hThread = CreateThread(NULL, 0, Thread_Read, NULL, 0, &ThreadID);


while(1)

{

caString[0] = getch();

if(0 == WriteFile(hComm, caString, 1, &dwWritten, 0))

{

printf("쓰기 에러\n");

}

else

{

printf("쓰기 성공\n");

}

}


CloseHandle(hComm);


return 0;

}


DWORD WINAPI Thread_Read(LPVOID NotUse)

{

char buf = 0;

DWORD dwRead;


while(1)

{

Sleep(100);

ReadFile(hComm, &buf, 1, &dwRead, NULL);

if(0 != dwRead)

{

printf("[%c] ", buf);

}

}

}







설정

트랙백

댓글

20141010 (C++ this)

155일차










-------------

C++

-------------


------- this

현재 객체를 가리킨다

(객체 주소)


예제)

#include <iostream>

#include <cstring>


using namespace std;


class SoSimple

{

public:

int num;


public:

SoSimple(int n)

: num(n)

{

cout << "num=" << num << ", ";

cout << "address=" << this << endl;

}


void ShowSimpleData()

{

cout << num << endl;

}


SoSimple * GetThisPointer()

{

return this;

}

};


int main()

{

SoSimple sim1(100);

SoSimple * ptr1 = sim1.GetThisPointer();


cout << ptr1 << ", ";

ptr1->ShowSimpleData();


SoSimple sim2(200);

SoSimple * ptr2 = sim2.GetThisPointer();


cout << ptr2 << ", ";

ptr2->ShowSimpleData();


return 0;

}



결과







다른 예제)

#include <iostream>


using namespace std;


class SelfRef

{

private:

int num;


public:

SelfRef(int n)

: num(n)

{

cout << "객체생성" << endl;

}


SelfRef & Adder(int n)

{

num = num + n;

return *this;

}


SelfRef & ShowTwoNumber()

{

cout << num << endl;

return *this;

}

};


int main()

{

SelfRef obj(3);

SelfRef & ref = obj.Adder(2);


obj.ShowTwoNumber();

ref.ShowTwoNumber();


ref.Adder(1).ShowTwoNumber().Adder(2).ShowTwoNumber();


return 0;

}



결과









--------------

RFID

--------------



beep.txt


ISO15693-HostCommand.pdf







설정

트랙백

댓글