Intel® C++ Compiler
Community support and assistance for creating C++ code that runs on platforms based on Intel® processors.
7944 Discussions

Remark #444: destructor for base class "SomeClass" (declared at line nnnn) is not virtual

SergeyKostrov
Valued Contributor II
970 Views

I have a Test-Case to verify a '_declspec( novtable )' declaration for a base class.

So, I declared a class CClassA with the '_declspec( novtable )' specificator and I didn't use
a 'virtual' keyword for any members of the class including the destructor. The Intel C++ compiler
displays a 'remark #444':

remark #444: destructor for base class "SomeClass" (declared at line nnnn) is not virtual

Here is some real example of the output:

------ Build started: Project: IccTestApp, Configuration: Debug Win32 ------
Compiling with Intel C++ Compiler XE 12.1.3.300 [IA-32]... (Intel C++ Environment)
IccTestApp.cpp
...
../PrtTests.cpp(1582): remark #444: destructor for base class "CClassA" (declared at line 1567) is not virtual
class CClassB : public CClassA
^
...
Linking... (Intel C++ Environment)
xilink: executing 'link'
Creating library ..\\Debug\\IccTestAppD.lib and object ..\\Debug\\IccTestAppD.exp
Embedding manifest... (Microsoft VC++ Environment)
IccTestApp - 0 error(s), 0 warning(s), 1 remark(s)

In case whena baseclass is declared with '_declspec( novtable )' specificator Intel C++ compiler
shouldn't display the remark.

If I declare a destructor of the class CClassA as virtual the Virtual Table will be created and it will override
the '_declspec( novtable )' declaration for the class.

Best regards,
Sergey

0 Kudos
1 Solution
Judith_W_Intel
Employee
970 Views

Thank you for the smalltest case. I have entered your request in our bug tracking system
as DPD200232942. We will let you know when it is resolved. Thanks again for reporting this.

Judy

View solution in original post

0 Kudos
13 Replies
Om_S_Intel
Employee
970 Views
It would be nice if you could provide sample testcase.
0 Kudos
SergeyKostrov
Valued Contributor II
970 Views
Please take a look at a test case:

[cpp] ... class _declspec( novtable ) CClassA { public: CClassA(){}; ~CClassA(){}; }; class CClassB : public CClassA { public: CClassB(){}; ~CClassB(){}; void MethodB() { printf( "[ CClassB::MethodB ] of the CClassB Executedn" ); }; }; ... CClassB *pB = new CClassB(); pB->MethodB(); if( pB != NULL ) delete pB; ... [/cpp]
0 Kudos
SergeyKostrov
Valued Contributor II
970 Views

My Development Environment:

OS: Windows XP 32-bit
IDE: Visual Studio 2005 SP1
Compilers: Intel C++ / Microsoft C++ / Borland C++ / MinGW / Turbo C++

Intel C++ compiler version ( Composer XE 2011 Update 9 ):

Intel C++ Compiler XE 12.1.3.300 [IA-32]

Intel C++ compiler command line options:

/c /Od /D "WIN32" /D "_CONSOLE" /D "_DEBUG" /D "_VC80_UPGRADE=0x0710"
/D "_UNICODE" /D "UNICODE" /GF /EHsc /RTC1 /MTd /GS /fp:precise /W5 /nologo /ZI /TP
/Qopenmp /Qdiag-disable:111,673

0 Kudos
Judith_W_Intel
Employee
971 Views

Thank you for the smalltest case. I have entered your request in our bug tracking system
as DPD200232942. We will let you know when it is resolved. Thanks again for reporting this.

Judy
0 Kudos
SergeyKostrov
Valued Contributor II
970 Views
Thank you, Judy!

Best regards,
Sergey
0 Kudos
Judith_W_Intel
Employee
970 Views

We sent this to EDG, our front end provider, and this was their reply:

We've discussed this here and think that the remark is warranted for this case.

According to the MSDN documentation, __declspec(novtable) suppresses setting the vtable pointer by the constructors and destructor of the affected class. The intent is that it will be applied only to "pure interface" classes, i.e., classes for which no object of that type will be created.

If anything, this would seem to argue more strongly in favor of issuing the remark in this case: the novtable attribute indicates that Derived objects are intended to be accessed via Base pointers/references. That is exactly the situation in which the Base destructor needs to be virtual, so that deleting a Derived object via a pointer to Base will work correctly.

In light of that analysis, we think we shouldn't change this remark unless you can point out a rationale that we've overlooked.

Judy

0 Kudos
SergeyKostrov
Valued Contributor II
970 Views
...According to the MSDN documentation, __declspec(novtable) suppresses setting the vtable pointer by the constructors and destructor of the affected class...


Hi Judy,

Did you try to debug the Test-Case? If, No, please do and take alook in a Watch Window. You will see how it
actually works. Try to use different combinbations of '_declspec( novtable )' and 'virtual' keywords for a destructor or
methods of a baseclass.

Best regards,
Sergey

0 Kudos
SergeyKostrov
Valued Contributor II
970 Views
...In light of that analysis, we think we shouldn't change this remark unless you can point out a rationale that we've overlooked...

You've looked at some references but you didn't look at how '_declspec( novtable )' is used on a regular non-interface class!
I don't think that EDG investigated that matter as well. An MSDN doesn't say anything that it can't be used on C++ classes.

I will also do additional investigation this week.

Best regards,
Sergey
0 Kudos
SergeyKostrov
Valued Contributor II
970 Views

Please take a look at a new Test-Case:

[cpp] #define _TEST_CASE_1 // Only one macro has to be uncommented! // #define _TEST_CASE_2 // #define _TEST_CASE_3 // #define _TEST_CASE_4 #ifdef _TEST_CASE_1 // Test-Case 1 class CClassA // Note 1: Vtable - No No errors at Run-time { public: CClassA(){}; ~CClassA(){}; void MethodA() { printf( "[ CClassA::MethodA ] of the CClassA Executedn" ); }; }; #endif #ifdef _TEST_CASE_2 // Test-Case 2 class CClassA // Note 2: Vtable - Yes No errors at Run-time { public: CClassA(){}; ~CClassA(){}; virtual void MethodA() { printf( "[ CClassA::MethodA ] of the CClassA Executedn" ); }; }; #endif #ifdef _TEST_CASE_3 // Test-Case 3 class _declspec( novtable ) CClassA // Note 3: Vtable - No No errors at Run-time { public: CClassA(){}; ~CClassA(){}; void MethodA() { printf( "[ CClassA::MethodA ] of the CClassA Executedn" ); }; }; #endif #ifdef _TEST_CASE_4 // Test-Case 4 class _declspec( novtable ) CClassA // Note 4: Vtable Yes ( Not initialied! ) Access Violation at Run-time { public: CClassA(){}; ~CClassA(){}; virtual void MethodA() { printf( "[ CClassA::MethodA ] of the CClassA Executedn" ); }; }; #endif void main( void ) { CClassA *pA = new CClassA(); pA->MethodA(); if( pA != NULL ) delete pA; } [/cpp]


In the Test-Case 4 when '_declspec( novtable )' and 'virtual' are used for the CClassA a Virtual Table
is created anyway, but it is NOT initialized. It means, that 'virtual' specificator overrides the '_declspec( novtable )' specificator.
Since the Virtual Table is NOT initialized an Access Violation happens at Run-time.

You could also try to declare the destructor as 'virtual' like:

class _declspec( novtable ) CClassA
{
public:
CClassA(){};
virtual ~CClassA(){};

void MethodA()
{
printf( "[ CClassA::MethodA ] of the CClassA Executed\n" );
};
};

The Virtual Table of the CClassAisNOT initialized as well.At Run-time the Access Violation will happen on 'delete pA'.

Once again, for a working case like:

class _declspec( novtable ) CClassA
{
public:
CClassA(){};
~CClassA(){};

void MethodA()
{
printf( "[ CClassA::MethodA ] of the CClassA Executed\n" );
};
};

Intel C++ compiler displays the Remark #444 that the destructor isNOT declared as 'virtual' and, if I add
that declaration, the code will stop working. For the last case ( just above )'_declspec( novtable )' declaration is useless, and
theRemark #444 creates more "troubles".

0 Kudos
SergeyKostrov
Valued Contributor II
970 Views

Here is a screenshot:

0 Kudos
Judith_W_Intel
Employee
970 Views

Sorry for the delay in replying.

I sent your example and comments to EDG, here is their reply:

> /*

> This customer gave this motivating example, which you can see if they

> add virtual to the Base class destructor that is declared as novtable

> this causes a seg fault since the virtual table is created anyway, but not initialized.

>

> I know the Microsoft documentation suggests that novtable should only

> be used on abstract base classes but it apparently doesn't enforce this.

>

> !% cl -DOK ex.cpp && ./ex

> Microsoft 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42

> for 80x86 Copyright (C) Microsoft Corporation. All rights reserved.

>

> ex.cpp

> Microsoft Incremental Linker Version 8.00.50727.42 Copyright (C)

> Microsoft Corporation. All rights reserved.

>

> /out:ex.exe

> ex.obj

> [ Base::MethodA ] of the Base Executed !% cl ex.cpp && ./ex Microsoft

> 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42 for 80x86

> Copyright (C) Microsoft Corporation. All rights reserved.

>

> ex.cpp

> Microsoft Incremental Linker Version 8.00.50727.42 Copyright (C)

> Microsoft Corporation. All rights reserved.

>

> /out:ex.exe

> ex.obj

> [ Base::MethodA ] of the Base Executed Segmentation fault

>

> Judy

> */

>

>

> extern "C" int printf(const char*,...); #define NULL 0

>

> struct _declspec( novtable ) Base // Vtable Yes ( Not initialied! )

> // Access Violation at Run-time

> {

> Base(){};

> #ifdef OK

> #else

> virtual

> #endif

> ~Base(){};

> void MethodA()

> { printf( "[ Base::MethodA ] of the Base Executed\n" );

> };

> };

>

>

>

> int main()

> {

> Base *pA = new Base();

> pA->MethodA();

> if( pA != NULL )

> delete pA;

> return 0;

> }

Thanks for the example. However, I think the original analysis still holds -- the remark will only be issued if there is a class derived from Base, in which case there's the danger of deleting via a pointer to Base and consequently not executing the derived class destructor. That failure could be regarded as more serious than the one above because it's a quiet failure: your code just quietly leaks memory or resources or whatever; at least the example above fails loudly. And it's perfectly legitimate to have a virtual destructor in a novtable class, as long as novtable is being used according to the intent expressed in the MSDN documentation. novtable is really a "trust me, I know what I'm doing" instruction to the compiler; there's not much that can be done if they're mistaken about knowing what they're doing. :-)

0 Kudos
SergeyKostrov
Valued Contributor II
970 Views
...novtable is really a "trust me, I know what I'm doing" instruction to the compiler...


Thank you, Judith! I'm completely satisfied withresponse.

Best regards,
Sergey

0 Kudos
SergeyKostrov
Valued Contributor II
968 Views
...novtable is really a "trust me, I know what I'm doing" instruction to the compiler...


Thank you, Judith! I'm completely satisfied withresponse...


Could you review the case again, please? ( I've 'bolded' and 'underlined' an interesting statement ).

[ From a thread http://software.intel.com/en-us/forums/showthread.php?t=105257&o=a&s=lr]

...
And one more to look at:

https://bug704127.bugzilla.mozilla.org/attachment.cgi?id=575855

Herearetwo very interesting pieces of codes from a patch:

[ 1 ]
...
#elif defined(_MSC_VER)
# if _MSC_VER >= 1400 [SergeyK] For VS2005, VS2008, etc
# define MOZ_HAVE_CXX11_OVERRIDE
+ /* MSVC currently spells "final" as "sealed". */
+# define MOZ_HAVE_CXX11_FINAL sealed
# endif
#endif
...

[ 2 ]
...
+/*
+ * MOZ_FINAL indicates that some functionality cannot be overridden through
+ * inheritance. It can be used to annotate either classes/structs or virtual
+ * member functions.
+ *
+ * To annotate a class/struct with MOZ_FINAL, place MOZ_FINAL immediately after
+ * the name of the class, before the list of classes from which it derives (if
+ * any) and before its opening brace. MOZ_FINAL must not be used to annotate
+ * unnamed classes or structs. (With some compilers, and with C++11 proper, the
+ * underlying expansion is ambiguous with specifying a class name.)
+ *
+ * class Base MOZ_FINAL
+ * {
+ * public:
+ * Base();
+ * ~Base();
+ * virtual void f() { }
+ * };
+ * // This will be an error in some compilers:
+ * class Derived : public Base
+ * {
+ * public:
+ * ~Derived() { }
+ * };
+ *
+ * One particularly common reason to specify MOZ_FINAL upon a class is to tell
+ * the compiler that it's not dangerous for it to have a non-virtual destructor
+ * yet have one or more virtual functions, silencing the warning it might emit
+ * in this case. Suppose Base above weren't annotated with MOZ_FINAL. Because
+ * ~Base() is non-virtual, an attempt to delete a Derived* through a Base*
+ * wouldn't call ~Derived(), so any cleanup ~Derived() might do wouldn't happen.
+ * (Formally C++ says behavior is undefined, but compilers will likely just call
+ * ~Base() and not ~Derived().) Specifying MOZ_FINAL tells the compiler that
+ * it's safe for the destructor to be non-virtual.
+ *
+ * In compilers implementing final controls, it is an error to inherit from a
+ * class annotated with MOZ_FINAL. In other compilers it serves only as
+ * documentation.
+ *
+ * To annotate a virtual member function with MOZ_FINAL, place MOZ_FINAL
+ * immediately before the ';' terminating the member function's declaration, or
+ * before '= 0;' if the member function is pure. If the member function is
+ * defined in the class definition, it should appear before the opening brace of
+ * the function body. (This placement is identical to that for MOZ_OVERRIDE.
+ * If both are used, they should appear in the order 'MOZ_FINAL MOZ_OVERRIDE'
+ * for consistency.)
+ *
+ * class Base
+ * {
+ * public:
+ * virtual void f() MOZ_FINAL;
+ * };
+ * class Derived
+ * {
+ * public:
+ * // This will be an error in some compilers:
+ * virtual void f();
+ * };
+ *
+ * In compilers implementing final controls, it is an error for a derived class
+ * to override a method annotated with MOZ_FINAL. In other compilers it serves
+ * only as documentation.
+ */
+#if defined(MOZ_HAVE_CXX11_FINAL)
+# define MOZ_FINAL MOZ_HAVE_CXX11_FINAL
+#else
+# define MOZ_FINAL /* no support */
+#endif
+
...

0 Kudos
Reply