가상 할당 연산자 C ++
C ++의 할당 연산자는 가상으로 만들 수 있습니다. 왜 필요한가요? 다른 운영자도 가상으로 만들 수 있습니까?
할당 연산자는 가상으로 만들 필요가 없습니다.
아래 설명은에 대한 내용 operator=
이지만 해당 유형을받는 연산자 오버로딩과 해당 유형을받는 모든 함수에도 적용됩니다.
아래 논의는 가상 키워드가 일치하는 함수 서명을 찾는 것과 관련하여 변수의 상속에 대해 알지 못함을 보여줍니다. 마지막 예제에서는 상속 된 유형을 처리 할 때 할당을 처리하는 방법을 보여줍니다.
가상 함수는 상속 변수의 상속에 대해 알지 못합니다.
가상 기능이 작동 광고 함수의 서명이 동일해야합니다. operator =가 가상으로 만들어 지므로 operator =의 변수와 반환 값이 다르기 때문에 호출이 D에서 가상 함수로 작동하지 않습니다.
고유 한 함수 B::operator=(const B& right)
및 D::operator=(const D& right)
100 % 완전히 다른 2 개 함수로 볼 수 있습니다.
class B
{
public:
virtual B& operator=(const B& right)
{
x = right.x;
return *this;
}
int x;
};
class D : public B
{
public:
virtual D& operator=(const D& right)
{
x = right.x;
y = right.y;
return *this;
}
int y;
};
상관 및 오버로드 된 연산자 2 개 :
B 유형의 변수에 할당 될 때 D의 사용을 정의 할 수 있습니다. 이것은 B 변수가 실제로 B의 참조에 D 인 경우에도 마찬가지입니다. D::operator=(const D& right)
함수.
아래의 경우, 2 개의 B 참조 D::operator=(const B& right)
유전자 유전자가 할당됩니다.
//Use same B as above
class D : public B
{
public:
virtual D& operator=(const D& right)
{
x = right.x;
y = right.y;
return *this;
}
virtual B& operator=(const B& right)
{
x = right.x;
y = 13;//Default value
return *this;
}
int y;
};
int main(int argc, char **argv)
{
D d1;
B &b1 = d1;
d1.x = 99;
d1.y = 100;
printf("d1.x d1.y %i %i\n", d1.x, d1.y);
D d2;
B &b2 = d2;
b2 = b1;
printf("d2.x d2.y %i %i\n", d2.x, d2.y);
return 0;
}
인쇄물 :
d1.x d1.y 99 100
d2.x d2.y 99 13
D::operator=(const D& right)
사용되지 않는 것을 보여줍니다 .
가상 키워드가 키워드 B::operator=(const B& right)
와 동일한 결과를 얻을 수 있습니다. 즉 그것은B::operator=(const B& right)
모든 것을 하나로 묶는 마지막 단계 인 RTTI :
RTTI를 사용하여 유형을 취하는 가상 기능을 입력하게 처리 할 수 있습니다. 다음은 상속 된 유형을 다룰 때 할당을 처리하는 방법을 알아내는 퍼즐의 마지막 조각입니다.
virtual B& operator=(const B& right)
{
const D *pD = dynamic_cast<const D*>(&right);
if(pD)
{
x = pD->x;
y = pD->y;
}
else
{
x = right.x;
y = 13;//default value
}
return *this;
}
운영자에 따라 증가합니다.
연산자를 가상으로 만드는 요점은 더 많은 필드를 복사하도록 재정의 할 수있는 할당을 누릴 수있는 것입니다.
따라서 기본 및 동적 유형이 파생되고 더 많은 필드가있는 경우 올바른 항목이 복사됩니다.
그러나 LHS가 Derived이고 RHS가 Base라는 위험이 있으므로 가상 연산자가 Derived에서 실행될 때 매개 변수는 Derived가없는 필드를 사용하지 않습니다.
다음은 좋은 토론입니다. http://icu-project.org/docs/papers/cpp_report/the_assignment_operator_revisited.html
Brian R. Bondy 는 다음과 같이 썼습니다.
모든 것을 하나로 묶는 마지막 단계 인 RTTI :
RTTI를 사용하여 유형을 취하는 가상 기능을 입력하게 처리 할 수 있습니다. 다음은 상속 된 유형을 다룰 때 할당을 처리하는 방법을 알아내는 퍼즐의 마지막 조각입니다.
virtual B& operator=(const B& right) { const D *pD = dynamic_cast<const D*>(&right); if(pD) { x = pD->x; y = pD->y; } else { x = right.x; y = 13;//default value } return *this; }
이 솔루션에 몇 가지 언급을 추가하고 싶습니다. 할당 연산자를 위와 동일하게 선언하면 세 가지 문제가 있습니다.
컴파일러는 가상이 아니고 사용자가 생각하는대로 수행하지 않는 const D & 인수를 사용하는 할당 연산자를 생성합니다 .
두 번째 문제는 반환 유형입니다. 파생 인스턴스에 대한 기본 참조를 반환합니다. 어쨌든 코드가 작동 문제가 많지 않을 것입니다. 그래도 그에 따라 참조를 반환하는 것이 좋습니다.
세 번째 문제는 파생 유형 할당 연산자가 기본 클래스 할당 연산자를 호출하지 않습니다. 할당 연산자를 가상으로 선언하면 컴파일러가 자동으로 생성합니다. 이는 원하는 결과를 발생하는 할당 연산자에 대해 두 번 이상의 발생가 발생하지 않는 부작용입니다.
기본 클래스를 고려하면 (제가 인용 한 게시물의 것과 동일) :
class B
{
public:
virtual B& operator=(const B& right)
{
x = right.x;
return *this;
}
int x;
};
다음 코드는 내가 인용 한 RTTI 솔루션을 완성합니다.
class D : public B{
public:
// The virtual keyword is optional here because this
// method has already been declared virtual in B class
/* virtual */ const D& operator =(const B& b){
// Copy fields for base class
B::operator =(b);
try{
const D& d = dynamic_cast<const D&>(b);
// Copy D fields
y = d.y;
}
catch (std::bad_cast){
// Set default values or do nothing
}
return *this;
}
// Overload the assignment operator
// It is required to have the virtual keyword because
// you are defining a new method. Even if other methods
// with the same name are declared virtual it doesn't
// make this one virtual.
virtual const D& operator =(const D& d){
// Copy fields from B
B::operator =(d);
// Copy D fields
y = d.y;
return *this;
}
int y;
};
이것은 말할 수 있습니다. D에서 도출 할 때 const B & 를 사용하는 1 연산자 =, const D &를 사용 하는 1 연산자 = 및 const D2 &를 사용 하는 하나의 연산자 가 필요하기 때문에 완전한 솔루션이 아닙니다 . 결론은 분명합니다. 연산자 = () 오버로드의 수는 수퍼 클래스의 수 + 1과 가변적입니다.
D2가 D를 상속한다는 것을 고려하여 두 개의 상속 된 연산자 = () 메소드가 어떻게 생겼는지 보았습니다.
class D2 : public D{
/* virtual */ const D2& operator =(const B& b){
D::operator =(b); // Maybe it's a D instance referenced by a B reference.
try{
const D2& d2 = dynamic_cast<const D2&>(b);
// Copy D2 stuff
}
catch (std::bad_cast){
// Set defaults or do nothing
}
return *this;
}
/* virtual */ const D2& operator =(const D& d){
D::operator =(d);
try{
const D2& d2 = dynamic_cast<const D2&>(d);
// Copy D2 stuff
}
catch (std::bad_cast){
// Set defaults or do nothing
}
return *this;
}
};
= (const D2 &) 연산자가 필드를 복사 하는 것이 분명합니다 . 마치 거기에있는 것처럼 상상 상상. 상속 된 연산자 = () 오버로드에서 패턴을 확인할 수 있습니다. 슬프게도 우리는이 패턴을 처리 할 가상 템플릿 메서드를 정의 할 수 없습니다. 내가 본 유일한 솔루션 인 완전한 다형성 할당 연산자를 얻으려면 동일한 코드를 여러 번 복사하여 넣어야합니다. 다른 이항 연산자에도 적용됩니다.
편집하다
주석에서 정의 된 내용했듯이, 삶을 더 쉽게 만들기 위해 할 수있는 최소한의 방법은 최상위 슈퍼 클래스 할당 연산자 = ()를하고 다른 모든 슈퍼 클래스 연산자 = () 메서드에서 호출하는 것입니다. 또한 필드를 복사 할 때 _copy 메소드를 정의 할 수 있습니다.
class B{
public:
// _copy() not required for base class
virtual const B& operator =(const B& b){
x = b.x;
return *this;
}
int x;
};
// Copy method usage
class D1 : public B{
private:
void _copy(const D1& d1){
y = d1.y;
}
public:
/* virtual */ const D1& operator =(const B& b){
B::operator =(b);
try{
_copy(dynamic_cast<const D1&>(b));
}
catch (std::bad_cast){
// Set defaults or do nothing.
}
return *this;
}
virtual const D1& operator =(const D1& d1){
B::operator =(d1);
_copy(d1);
return *this;
}
int y;
};
class D2 : public D1{
private:
void _copy(const D2& d2){
z = d2.z;
}
public:
// Top-most superclass operator = definition
/* virtual */ const D2& operator =(const B& b){
D1::operator =(b);
try{
_copy(dynamic_cast<const D2&>(b));
}
catch (std::bad_cast){
// Set defaults or do nothing
}
return *this;
}
// Same body for other superclass arguments
/* virtual */ const D2& operator =(const D1& d1){
// Conversion to superclass reference
// should not throw exception.
// Call base operator() overload.
return D2::operator =(dynamic_cast<const B&>(d1));
}
// The current class operator =()
virtual const D2& operator =(const D2& d2){
D1::operator =(d2);
_copy(d2);
return *this;
}
int z;
};
기본 연산자 = () 오버로드에서 하나의 호출 만 수신하므로 set defaults 메서드 가 필요하지 않습니다 . 필드 복사시 변경 사항이 한곳에서 수행되고 모든 연산자 = () 오버로드가 영향을 받고 의도 된 목적을 수행합니다.
덕분에 sehe 제안합니다.
가상 할당은 아래 시나리오에서 사용됩니다.
//code snippet
Class Base;
Class Child :public Base;
Child obj1 , obj2;
Base *ptr1 , *ptr2;
ptr1= &obj1;
ptr2= &obj2 ;
//Virtual Function prototypes:
Base& operator=(const Base& obj);
Child& operator=(const Child& obj);
사례 1 : obj1 = obj2;
이 가상 개념에서 우리 operator=
가 Child
수업 을 부르는 것처럼 어떤 역할도하지 않습니다 .
사례 2 & 3 : * ptr1 = obj2;
* ptr1 = * ptr2;
여기서 할당은 예상대로되지 않습니다. 대신 수업 operator=
에서 이유 가 호출 Base
됩니다.
다음 중 하나를 사용하여 수정할 수 있습니다.
1) 주조
dynamic_cast<Child&>(*ptr1) = obj2; // *(dynamic_cast<Child*>(ptr1))=obj2;`
dynamic_cast<Child&>(*ptr1) = dynamic_cast<Child&>(*ptr2)`
2) 가상 개념
이제 virtual Base& operator=(const Base& obj)
서명이 Child
및 Base
에서 다르기 때문에 단순히 사용 하면 도움이되지 않습니다 operator=
.
Base& operator=(const Base& obj)
일반적인 Child& operator=(const Child& obj)
정의 와 함께 Child 클래스 를 추가해야합니다 . 기본 할당 연산자가 없으면 나중에 정의를 포함하는 것이 중요합니다 ( obj1=obj2
원하는 결과를 제공하지 않을 수 있음).
Base& operator=(const Base& obj)
{
return operator=(dynamic_cast<Child&>(const_cast<Base&>(obj)));
}
사례 4 : obj1 = * ptr2;
이 경우 컴파일러는 Child에서 호출 operator=(Base& obj)
된 Child
대로 정의를 찾습니다 operator=
. 그 존재하지 및하지만 이후 Base
유형으로 승격 할 수없는 child
암시, 그것은 착오를 통해 것입니다. (캐스팅이 같은 필요합니다 obj1=dynamic_cast<Child&>(*ptr1);
)
case2 & 3에 따라 구현하면이 시나리오가 처리됩니다.
보시다시피 가상 할당은 Base 클래스 pointers / reference를 사용하는 할당의 경우 호출을 더 우아하게 만듭니다.
다른 운영자도 가상으로 만들 수 있습니까? 예
클래스에서 파생 된 클래스가 모든 멤버를 올바르게 복사하도록 보장하려는 경우에만 필요합니다. 다형성으로 아무것도하지 않는다면, 이것에 대해 걱정할 필요가 없습니다.
원하는 연산자를 가상화하는 데 방해가되는 것은 없습니다. 특수한 경우의 메서드 호출 일뿐입니다.
이 페이지 는 이 모든 것이 어떻게 작동하는지에 대한 훌륭하고 상세한 설명을 제공합니다.
연산자는 특수 구문을 사용하는 방법입니다. 다른 방법과 같이 치료할 수 있습니다.
참고 URL : https://stackoverflow.com/questions/669818/virtual-assignment-operator-c
'ProgramingTip' 카테고리의 다른 글
이 C ++를 복사 할 수 있습니까? (0) | 2020.11.19 |
---|---|
IllegalArgumentException : 도장 1의 바인드 값이 null입니다. (0) | 2020.11.19 |
“geom_histogram”을 사용할 때“unit (tic_pos.c,”mm“) : 'x'및 'units'의 길이가 0보다 커야합니다. (0) | 2020.11.18 |
JavaScript 유형 타입 시스템은 고전적인 클래스 시스템을 모방하는 것 이상으로 무엇을 할 수 있습니까? (0) | 2020.11.18 |
리포지토리 패턴-이를 이해하는 방법과 "복잡한"엔터티와 어떻게 작동합니까? (0) | 2020.11.18 |