270 likes | 396 Views
Reference/Copy Constructor. C++ Programming 강 성 관. 지역 변수와 전역변수 (local varible & global varible). 지역 변수 어떤 함수내부에서 선언된 변수 소속된 함수 내부에서만 사용 가능 선언할 때 초기값을 주는 것이 가장 이상적 초기값을 안 줄 경우 정수형이나 문자형 변수 모두 쓰레기 값으로 자동으로 초기화 됨 전역 변수 어느 함수에도 속하지 않은 변수 선언할 때 초기값을 줄 수 있음 선언할 때 초기값을 안 줄 경우
E N D
Reference/Copy Constructor C++ Programming 강 성 관
지역 변수와 전역변수 (local varible & global varible) • 지역 변수 • 어떤 함수내부에서 선언된 변수 • 소속된 함수 내부에서만 사용 가능 • 선언할 때 초기값을 주는 것이 가장 이상적 • 초기값을 안 줄 경우 • 정수형이나 문자형 변수 모두 쓰레기 값으로 자동으로 초기화 됨 • 전역 변수 • 어느 함수에도 속하지 않은 변수 • 선언할 때 초기값을 줄 수 있음 • 선언할 때 초기값을 안 줄 경우 • 정수형 변수는 자동으로 0으로 초기화 됨 • 문자형 변수는 자동으로 ‘\0’(null 문자) 값으로 초기화 됨. • 프로그램 내에 있는 모든 함수가 사용할 수 있음 • 변경된 값이 어느 함수에서 출력해봐도 일정한 값이 출력됨.
정적 기억 공간 & 정적 변수 • 프로그램이 실행되는 동안에 지속적으로 존재하는 공간. • 정적 변수 • 프로그램이 실행되는 전체 시간 동안 계속 존속한다. • 스택(stack)에 저장되지 않는다. • 넉넉하게 크기가 정해진 메모리 블록은 프로그램이 실행되는 동안에 크기가 변하지 않는다. • 명시적으로 초기화하지 않으면 모두 0으로 설정된다. • 명시적으로 초기화 했을 경우 실행할 때 오직 한번만 초기화 된다. (1)전역 변수 함수의 외부에서 정의 (2) static 키워드를 붙여서 함수 내에서 정의. (3) static 키워드를 붙여서 함수 외부에서 정의. static int fee = 56 ; Ex) static double fee = 56.50 ;
Stack(스택)의 정의 (1) ① 프로그램에서 각 함수에 의해 필요로 되는 자료들을 보관하기 위해 할당된 메모리의 특수한 영역 ② 입▪출력(Insertion/Deletion)이 리스트의 한쪽 끝으로만 제한된 리스트 last-in-first-out [LIFO] 구조 입력 출력 (insertion) (deletion) <스택의 동작 구조> ③ limit : 입▪출력이 허용되는 리스트의 끝 BOTTOM : TOP의 반대쪽 끝 ④ Stack pointer : 가장 최근에 입력된 value의 위치를 가리키는 포인터 (출력 우선 순위가 가장 높은 value를 가리키는 포인터) ⑤ 컴파일러, 서브루틴의 복귀주소(return address)기억에 사용
Stack (2) index stack stack stack stack 6 5 4 F 3 E D 2 C B 0 2 3 4 C TOP 1 A A TOP A TOP ⒜ ⒟ ⒝ ⒞
Stack (3) ⑴ 입력과 출력의 개념 ⓐ 초기상태 TOP = nil ⓑ A를 입력하고 (TOP=1) 계속해서 B를 입력한 상태 ⓒ B를 출력하고 (TOP=1) C를 입력(TOP=2) 한 다음 이어서 D를 입력한 상태 ⓓ D를 출력하고 (TOP=2) E를 입력(TOP=3) 한 다음 이어서 F를 입력한 상태 ⑵ 스택에서 입력과 출력의 알고리즘 ① 입력(insert, push, put) : 최대 인덱스값(stack length)을 N이라 하면 ㉮ IF TOP = nil이면 TOP=0; ㉯ TOP ← TOP+1; ㉰ TOP>N 이면 overflow 이므로 exit; TOP≤N 이면 STACK[TOP] ← new atom; ② 출력(delete, pop, pull) ㉮ IF TOP = nil이면 underflow 이므로 exit; ㉯ remove STACK[TOP]; ㉰ TOP ← TOP -1; ㉱ IF TOP = 0이면 TOP ← nil;
참조자(reference) • 변수의 또 다른 이름으로 사용되는 내재된 포인터 • 일종의 또 다른 이름 “ 별칭(alias)” • 참조자를 만들때 다른 변수의 이름(타겟) 으로 초기화 하고 이 때부터 참조자는 이 타겟에 대한 다른 이름으로 행세. • 참조자(reference)의 사용 -함수에게 전달될 때(call by reference) • w인수의 주소를 전달해야 한다는 사실을 더 이상 기억할 필요가 • 없다. w코드의 판독성 및 유지 보수성을 향상시킬 수 있다. w객체가 참조로 함수에게 전달될 때 복사본이 만들어지지 않는다(객체의 주소만을 넘겨 준다). -함수로부터 반환될 때 • 참조자(reference)의 선언- 타겟 변수의 형을 쓰고 참조 연산자(&) 다음에 참조자의 이름을 쓴다. 예 ) int someInt ; int &rSomeRef = someInt ; => ‘rSomeRef는 someInt를 참조하도록 초기화된 정수형 참조자’
포인터와 참조자(reference) • 공통점 • 둘 다 메모리 주소를 다룬다. • 차이점 • 사용하는 방식이 다르다. • 포인터 : 별도의 메모리 공간을 차지 • 참조자 : 별도의 메모리 공간을두는 것이 아니라 같은 공간을 공유 • 포인터에서의 &(주소 연산자) • 메모리 주소를 얻어오는 경우pvalue = &nValue ; Test(&nValue) ; • 참조 연산자(&) • 변수를 선언할 때, 함수에 인자를 넘겨줄 때 int &Reference ; void Test(int &Reference) ;
참조형 인자를 사용한 함수 호출 • 함수 인자에서 참조 연산자를 선언 • 함수를 부르는 쪽에서는 일반 인자를 넘기는 것과 같다. • 함수를 부를 때 인자가 포인터형 일 때는 변수 주소나 포인터가 오고, 참조형 일 때는 반드시 변수가 온다 • 참조형을 이용하는 경우 void increase(int &nVal) { nVal++ ; } void main() { int nValue ; increase(nValue) ; }
참조자(3) • 포인터와 참조자 #include<iostream.h> void f(int &n); main() { int i = 0; f(i); cout << “Here is i’s new value:” << i << “\n”; return 0; } void f(int &n) //n의 메모리 생성 안됨 { n = 100; } #include<iostream.h> void f(int *n); main() { int i = 0; f(&i); cout << “Here is i’s new value:” << i << “\n”; return 0; } void f(int *n) //n의 메모리 생성 { *n = 100; }
객체에 대한 참조의 전달(1) • 참조로 객체를 전달 -생성자 함수 및 소멸자 함수 호출 안 함 • Call-by-Value 방식 -생성자 함수 호출 함 -소멸자 함수 호출 함 • 예제 프로그램 #include<iostream.h> class myclass { int who; public : myclass(int n) { who = n; cout << “Constructing” << who << “\n”; } ~myclass() { cout << “Destructing…\n”; } int id() { return who; } };
void f(myclass &o) // 생성자 함수 호출 안 함, Why? { cout << “Received” << o.id() << “\n”; } // 소멸자 함수 호출 안 함. main() { myclass x(1); // 생성자 함수 호출 f(x); return 0; } // 소멸자 함수 호출 Result :
참조의 반환 • 연산자 중복을 정의할 때 유용 • 예제 프로그램 #include<iostream.h> int &f(); int x; main() { f() = 100;// x = 100; 과 동일 cout << x << “\n”; return 0; } int &f() { return x;// x에 대한 참조를 반환한다. } Result :
객체의 치환(1) • 하나의 객체는 같은 형의 다른 객체로 치환 - 모든 데이터 멤버들은 비트 단위로 복사 #include<iostream.h> class myclass { int a, b; public: void set(int i, int j) { a = i; b = j; } void show() { cout << a << ‘ ’ << b << “\n”; } };
객체의 치환(2) main() { myclass o1, o2; o1.set(10,4); o2 = o1;// o1을 o2에 치환 o1.show(); o2.show(); return 0; } - 같은 형의 객체만 사용 -복사가 이루어질 때에는 객체 안의 모든 데이터 멤버가 다른 객체로 치환된다. w멤버가 문자열 일지라도 복사가 이루어진다. w포인터 변수에 대해서는 복사 시 run-time 에러 발생 - 해결 : 복사 생성자 사용
예제 : 메모리할당을 통한 문자열 저장(1) strtype :: strtype(char *ptr) { len = strlen(ptr); p = new char[ len+1 ] ; if(!p) { cout << “Allocation error\n”; exit(1); } strcpy(p, ptr); } #include<iostream.h> #include<iostream.h> #include<string.h> #include<stdlib.h> class strtype { char *p; int len; public: strtype(char *ptr); ~strtype(); void show(); }; 뒤에 계속
예제 : 메모리할당을 통한 문자열 저장(2) strtype :: ~strtype() { cout << “Freeing p\n”; delete p ; } void strtype :: show() { cout <<p << “-length :” <<len; cout << “\n”; } 뒤에 계속
예제 : 메모리할당을 통한 문자열 저장(3) main() { strtype s1(“This is a test”), s2(“I like C++”); s1.show(); s2.show(); s2 = s1; // 객체 치환에 의한 run-time에러 발생 // 해결 : 복사 생성자 사용 s1.show(); s2.show(); return 0; }
할당 연산자(=)와 복사생성자(copy constructor)(1) • ‘=‘연산자를 이용해서 할당 연산자나 복사 생성자를 호출한다. • 형식 • Person temp , p ; • temp = p ; //할당연산자 호출 • temp.show() ; • (2) Person p; • Person temp = p ; //복사생성자 호출 • tmp.show() ; • 할당 연산자 함수는 객체 값을 할당한 다음 객체 자신(*this)을 리턴하는데 이 값은 다음 할당 연산자 함수에 인자로 넘겨집니다. • 리턴된 객체는 다시 다음 할당 연산자 함수에 인자로 넘겨진다. 예) Person temp1, temp2 , anony(“하마입”) ; temp2 = temp1 = anony ;
할당 연산자(=)와 복사생성자(copy constructor)(2) (1)할당 연산자 함수 class Person { char *mouth ; int height ; public : Person const &operator=(Person const &other) ; ~Person() { delete[] mouth ; } }; 선언 부분 Person const &Person::operator=(Person const &other) { mouth = other.mouth ; height = other.height ; } 정의 부분
할당 연산자(=)와 복사생성자(copy constructor)(3) • 할당 연산자 함수의문제점 • 어떤 클래스의 멤버 변수가 포인터 변수가 없을 때는 문제가없으나 포인터 변수가 있을 때는 복사시 포인터 변수끼리 주소값이복사되어 같은 공간을 공유하게 된다. • 소멸자가 동작할 때 첫번째 객체가 지운 500번지 공간을 다음 객체의 소멸자가 또 지우려 할 때 문제가 발생. other.mouth 500번지 mouth 500번지 500번지
할당 연산자(=)와 복사생성자(copy constructor)(4) (2) 복사 생성자 함수 Person(Person const &other) ; 선언 부분 Person::Person(Person const &other) //복사 생성자 정의 부분 { pName = AllocString(other.pName) ; nAge = other.nAge ; } 정의 부분 • 복사 생성자 함수 • 자신과 같은 형(type = class)의 객체를 참조에 의해 인자로 받아들이는 생성자
복사 생성자(Copy Constructor) • 객체의 복사본이 만들어 질 때마다 항상 호출되며 어떤 것이 일어 나야 할지를 정확하게 지정할 수 있다. • -형식 • classname (const classname &ob) { // body of constructor } • 예) CAT ( const CAT &theCat ) ; • 모든 복사 생성자는 하나의 매개변수를 가지는데 같은 클래스 타입의 객체에 대한 참조자 이다. • 이 매개변수를 상수 참조자로 만들어 놓는다. • 왜? => 생성자가 전달된 객체를 바꾸지 못하도록 • 예) CAT ( const CAT &theCat ) ; • CAT 생성자는 이미 존재하는 CAT 객체에 대해 상수 참조자를 가진다. • 복사 생성자의 목적은 theCat에 대한 복사본을 만드는 것. • 기본 복사 생성자는 매개변수로 전달된 객체의 각 멤버 변수들 중에서 포인터 변수가 가리키는 공간은 새로운 객체와 공유(aliasing)한다. (shallow copy).
복사 생성자(Copy Constructor)(2) • itsAge 는 CAT 클래스 안에 있는 멤버변수로서 포인터 변수 • old CAT과 New CAT은 각각 CAT 클래스의 첫번째와 두번째 객체 자유기억공간 • 기본 복사 생성자(얕은복사) • 두 개의 객체는 같은 메모리를 포인트 old CAT New CAT itsAge itsAge 자유기억공간 • 깊은 복사의 예 • 사용자가 정의한 복사생성자 • 두 개의 객체는 다른 메모리를 포인트 old CAT New CAT itsAge itsAge
함수로부터 객체의 반환(1) • 함수의 결과로서 객체를 반환할 수 있다. - 반환형으로 클래스형을 함수와 같이 선언 - return문을 사용 • 함수로부터 객체를 반환할 때 중요한 점 - 할당 연산에서함수에서 반환될 때 반환값을 저장하기 위한 임 시 객체(temporary object)가 자동으로 생성 • - 값이 반환된 후 임시객체는 소멸
복사 생성자의 사용 • 복사 생성자의 사용 이유 -객체가 함수의 인자로서 사용된 경우 w포인터 변수를 가지고 있는 객체가 함수의 인자로 전달될 경우 원본의 객체가 변경될 가능성이 있기 때문 -객체가 함수의 결과로서 반환될 때 • w할당 연산일 경우 함수에서 반환된 값을 저장하는 임시 객체 • 를 생성 자동적으로 이루어져, 프로그래머가 제어할 수 없음 w소멸자 함수가 동적으로 할당된 메모리를 해제하는 경우 장애가 발생할 수 있음