함수에 대한 C ++ 11의 "최종"키워드의 목적은 무엇입니까?
final
함수에 대한 C ++ 11 키워드 의 목적은 무엇입니까 ? 파생 클래스가 함수를 재정의하는 것을 방지한다는 것을 알고 있지만 이것이 사실이라면 비가 상 final
함수 로 선언하기에 충분하지 않습니까? 내가 여기서 놓친 또 다른 것이 있습니까?
idljarn이 이미 주석에서 언급했듯이 누락 된 것은 기본 클래스에서 함수를 재정의 하는 경우 비가 상으로 표시 할 수 없다는 것입니다.
struct base {
virtual void f();
};
struct derived : base {
void f() final; // virtual as it overrides base::f
};
struct mostderived : derived {
//void f(); // error: cannot override!
};
클래스가 상속되는 것을 막는 것입니다. 에서 위키 백과 :
C ++ 11은 클래스에서 상속받지 못하거나 파생 클래스에서 메서드를 재정의하는 것을 단순히 방지하는 기능도 추가합니다. 이것은 특수 식별자 final로 수행됩니다. 예를 들면 다음과 같습니다.
struct Base1 final { }; struct Derived1 : Base1 { }; // ill-formed because the class Base1 // has been marked final
또한 파생 클래스에서 가상 함수가 재정의되지 않도록 가상 함수를 표시하는 데 사용됩니다.
struct Base2 { virtual void f() final; }; struct Derived2 : Base2 { void f(); // ill-formed because the virtual function Base2::f has // been marked final };
Wikipedia는 또한 흥미로운 지적을합니다 .
언어 키워드 도
override
아닙니다final
. 그것들은 기술적으로 식별자입니다. 그들은 특정 상황에서 사용될 때만 특별한 의미를 얻습니다 . 다른 위치에서는 유효한 식별자가 될 수 있습니다.
즉, 다음이 허용됩니다.
int const final = 0; // ok
int const override = 1; // ok
"최종"은 또한 컴파일러 최적화가 간접 호출을 우회 할 수있게합니다.
class IAbstract
{
public:
virtual void DoSomething() = 0;
};
class CDerived : public IAbstract
{
void DoSomething() final { m_x = 1 ; }
void Blah( void ) { DoSomething(); }
};
"final"을 사용하면 컴파일러는 CDerived::DoSomething()
내부 Blah()
또는 인라인 에서 직접 호출 할 수 있습니다 . 그것 없이는 재정의 된 파생 클래스 내에서 호출 될 수 Blah()
있기 때문에 내부에서 간접 호출을 생성해야합니다 .Blah()
DoSomething()
"최종"의 시맨틱 한 측면에 추가 할 것은 없습니다.
그러나 "최종"이 그리 멀지 않은 미래에 매우 중요한 컴파일러 최적화 기술 이 될 수 있다고 chris green의 의견에 덧붙이고 싶습니다 . 그가 언급 한 간단한 경우뿐만 아니라 "최종"에 의해 "폐쇄"될 수있는보다 복잡한 실제 클래스 계층 구조에 대해서도 컴파일러는 일반적인 vtable 방식보다 더 효율적인 디스패치 코드를 생성 할 수 있습니다.
vtables의 주요 단점은 이러한 가상 객체 (일반적인 Intel CPU에서 64 비트로 가정)에 대해 포인터 만 캐시 라인의 25 % (64 바이트 중 8 바이트)를 소비한다는 것입니다. 내가 작성하는 응용 프로그램의 종류에서 이것은 매우 심하게 아프다. (그리고 내 경험상 그것은 순수한 프로그래머 관점에서, 즉 C 프로그래머에 의한 C ++에 대한 # 1 논쟁입니다.)
C ++에서는 그리 드문 일이 아닌 극단적 인 성능이 필요한 응용 프로그램에서는 실제로 C 스타일이나 이상한 템플릿 저글링 에서이 문제를 수동으로 해결하지 않아도 굉장히 좋아질 수 있습니다.
이 기술을 가상화 해제라고 합니다. 기억할 가치가있는 용어. :-)
Andrei Alexandrescu의 최근 발언이 있습니다. 오늘날 이러한 상황을 해결할 수있는 방법과 "최종"이 미래에 유사한 사례를 "자동으로"해결하는 방법에 대해 잘 설명하고 있습니다 (청중과 논의).
http://channel9.msdn.com/Events/GoingNative/2013/Writing-Quick-Code-in-Cpp-Quickly
비가 상 기능에는 Final을 적용 할 수 없습니다.
error: only virtual member functions can be marked 'final'
비가 상 방법을 '최종'으로 표시 할 수 있다는 것은 의미가 없습니다. 주어진
struct A { void foo(); };
struct B : public A { void foo(); };
A * a = new B;
a -> foo(); // this will call A :: foo anyway, regardless of whether there is a B::foo
a->foo()
항상 전화 A::foo
합니다.
그러나 A :: foo가 virtual
이면 B :: foo가이를 대체합니다. 이것은 바람직하지 않을 수 있으므로 가상 기능을 최종적으로 만드는 것이 합리적입니다.
문제는 가상 함수에 final을 허용하는 이유 입니다. 계층 구조가 깊은 경우 :
struct A { virtual void foo(); };
struct B : public A { virtual void foo(); };
struct C : public B { virtual void foo() final; };
struct D : public C { /* cannot override foo */ };
Then the final
puts a 'floor' on how much overriding can be done. Other classes can extend A and B and override their foo
, but it a class extends C then it is not allowed.
So it probably doesn't make sense to make the 'top-level' foo final
, but it might make sense lower down.
(I think though, there is room to extend the words final and override to non-virtual members. They would have a different meaning though.)
A use-case for the 'final' keyword that I am fond of is as follows:
// This pure abstract interface creates a way
// for unit test suites to stub-out Foo objects
class FooInterface
{
public:
virtual void DoSomething() = 0;
private:
virtual void DoSomethingImpl() = 0;
};
// Implement Non-Virtual Interface Pattern in FooBase using final
// (Alternatively implement the Template Pattern in FooBase using final)
class FooBase : public FooInterface
{
public:
virtual void DoSomething() final { DoFirst(); DoSomethingImpl(); DoLast(); }
private:
virtual void DoSomethingImpl() { /* left for derived classes to customize */ }
void DoFirst(); // no derived customization allowed here
void DoLast(); // no derived customization allowed here either
};
// Feel secure knowing that unit test suites can stub you out at the FooInterface level
// if necessary
// Feel doubly secure knowing that your children cannot violate your Template Pattern
// When DoSomething is called from a FooBase * you know without a doubt that
// DoFirst will execute before DoSomethingImpl, and DoLast will execute after.
class FooDerived : public FooBase
{
private:
virtual void DoSomethingImpl() {/* customize DoSomething at this location */}
};
final
adds an explicit intent to not have your function overridden, and will cause a compiler error should this be violated:
struct A {
virtual int foo(); // #1
};
struct B : A {
int foo();
};
As the code stands, it compiles, and B::foo
overrides A::foo
. B::foo
is also virtual, by the way. However, if we change #1 to virtual int foo() final
, then this is a compiler error, and we are not allowed to override A::foo
any further in derived classes.
Note that this does not allow us to "reopen" a new hierarchy, i.e. there's no way to make B::foo
a new, unrelated function that can be independently at the head of a new virtual hierarchy. Once a function is final, it can never be declared again in any derived class.
The final keyword allows you to declare a virtual method, override it N times, and then mandate that 'this can no longer be overridden'. It would be useful in restricting use of your derived class, so that you can say "I know my super class lets you override this, but if you want to derive from me, you can't!".
struct Foo
{
virtual void DoStuff();
}
struct Bar : public Foo
{
void DoStuff() final;
}
struct Babar : public Bar
{
void DoStuff(); // error!
}
As other posters pointed out, it cannot be applied to non-virtual functions.
One purpose of the final keyword is to prevent accidental overriding of a method. In my example, DoStuff() may have been a helper function that the derived class simply needs to rename to get correct behavior. Without final, the error would not be discovered until testing.
Final keyword in C++ when added to a function, prevents it from being overridden by a base class. Also when added to a class prevents inheritance of any type. Consider the following example which shows use of final specifier. This program fails in compilation.
#include <iostream>
using namespace std;
class Base
{
public:
virtual void myfun() final
{
cout << "myfun() in Base";
}
};
class Derived : public Base
{
void myfun()
{
cout << "myfun() in Derived\n";
}
};
int main()
{
Derived d;
Base &b = d;
b.myfun();
return 0;
}
Also:
#include <iostream>
class Base final
{
};
class Derived : public Base
{
};
int main()
{
Derived d;
return 0;
}
Supplement to Mario Knezović 's answer:
class IA
{
public:
virtual int getNum() const = 0;
};
class BaseA : public IA
{
public:
inline virtual int getNum() const final {return ...};
};
class ImplA : public BaseA {...};
IA* pa = ...;
...
ImplA* impla = static_cast<ImplA*>(pa);
//the following line should cause compiler to use the inlined function BaseA::getNum(),
//instead of dynamic binding (via vtable or something).
//any class/subclass of BaseA will benefit from it
int n = impla->getNum();
The above code shows the theory, but not actually tested on real compilers. Much appreciated if anyone paste a disassembled output.
'Programing' 카테고리의 다른 글
큰 HTML 문자열에서 jQuery 객체 만들기 (0) | 2020.07.04 |
---|---|
AttributeError : 'datetime'모듈에 'strptime'속성이 없습니다. (0) | 2020.07.04 |
레일스 모델이 같지 않은 곳을 찾습니다 (0) | 2020.07.04 |
람다가있는 foreach, array_map 및 정적 함수가있는 array_map의 성능 (0) | 2020.07.04 |
이클립스 : 모든 프로젝트가 프로젝트 탐색기에서 사라졌습니다. (0) | 2020.07.04 |