C++98下自定义dynamic_smart_pointer_cast链接错误及代码优化咨询
Hey there, let's work through your SmartPointer issues step by step—starting with that linker error that's blocking you, then fixing some logical bugs, and finally optimizing the implementation for C++98.
1. Fixing the Linker Error
The undefined reference error comes from two critical mismatches between your dynamic_smart_pointer_cast template definition and its friend declaration:
- Parameter type mismatch: Your friend declaration expects a
const SmartPointer<U>&, but your definition usesconst SmartPointer<U*>&(extra*on U). This means the compiler can't match your calldynamic_smart_pointer_cast<BaseClass>(DerivedClassInstance)to any existing template. - Template parameter alignment: The friend declaration uses
<class Y, class U>, while your definition uses<class T, class U>. While parameter names don't need to be identical, the signature must align to avoid compiler confusion. - Typo: You called
sp.reference->addRef()(lowercasea) but the RC method is namedAddRef()(uppercaseA). This would cause a compiler error once the linker issue is resolved.
Fix these by updating the cast function definition to match the friend declaration exactly:
template <class Y, class U> SmartPointer<Y> dynamic_smart_pointer_cast(const SmartPointer<U>& sp) { // ... corrected logic here }
2. Fixing Logical Bugs in dynamic_smart_pointer_cast
Your original cast function had broken reference count handling:
- You tried to
delete new_sp.pDatawhennew_spwas default-constructed (which setspDatato0—harmless but unnecessary). - You didn't properly clean up the default RC instance created by
new_spbefore adopting the source's reference count.
Here's the corrected version:
template <class Y, class U> SmartPointer<Y> dynamic_smart_pointer_cast(const SmartPointer<U>& sp) { SmartPointer<Y> new_sp; Y* casted_ptr = dynamic_cast<Y*>(sp.pData); if (casted_ptr != 0) { // Clean up the default reference count new_sp created if (new_sp.reference->Release() == 0) { delete new_sp.reference; } // Adopt the source's pointer and increment its reference count new_sp.pData = casted_ptr; new_sp.reference = sp.reference; new_sp.reference->AddRef(); } return new_sp; }
If the dynamic_cast fails, new_sp stays in its default empty state (null pData and a reference count of 0).
3. Critical Fixes & Optimizations for SmartPointer
Your implementation has a few hidden bugs and areas for improvement:
a. Uninitialized Reference Count
Your RC class doesn't initialize the count member variable! This means the reference count starts with a random garbage value, which will completely break the pointer's lifecycle. Fix this by adding a constructor to RC:
class RC { private: int count; // Reference count public: RC() : count(0) {} // Initialize count to 0 void AddRef() { __sync_add_and_fetch(&count, 1); } int Release() { return __sync_sub_and_fetch(&count, 1); } };
b. Incorrect operator!() Logic
Your operator!() returns true when the reference exists, but it should return true when the pointer is empty. Update it to:
bool operator!() const { return (pData == 0); }
c. Const-Correct Dereference Operators
Add const versions of operator* and operator-> so you can use const SmartPointer instances:
const T& operator*() const { return *pData; } const T* operator->() const { return pData; }
d. Broken Raw Pointer Comparison
Your operator== and operator!= for void* compare the reference pointer instead of the actual pData. Fix this to match expected behavior:
bool operator==(const void* other) const { return (pData == static_cast<const T*>(other)); } bool operator!=(const void* other) const { return !(*this == other); }
4. Full Optimized C++98-Compatible Implementation
Here's the complete, fixed header with all the above changes:
#ifndef __SMART_POINTER_HEADER__ #define __SMART_POINTER_HEADER__ class RC { private: int count; // Reference count public: RC() : count(0) {} // Initialize reference count to 0 void AddRef() { // Thread-safe increment (GCC extension, common in C++98 environments) __sync_add_and_fetch(&count, 1); } int Release() { // Thread-safe decrement return __sync_sub_and_fetch(&count, 1); } }; template <typename T> class SmartPointer { private: T* pData; // Raw pointer to the object RC* reference; // Reference count tracker public: // Default constructor: empty smart pointer SmartPointer() : pData(0), reference(0) { reference = new RC(); reference->AddRef(); } // Constructor from raw pointer (explicit to prevent implicit conversions) explicit SmartPointer(T* pValue) : pData(pValue), reference(0) { reference = new RC(); reference->AddRef(); } // Copy constructor SmartPointer(const SmartPointer<T>& sp) : pData(sp.pData), reference(sp.reference) { reference->AddRef(); } // Destructor ~SmartPointer() { if (reference) { if (reference->Release() == 0) { delete pData; delete reference; } reference = 0; pData = 0; } } // Dereference operators (non-const) T& operator*() { return *pData; } T* operator->() { return pData; } // Dereference operators (const) const T& operator*() const { return *pData; } const T* operator->() const { return pData; } // Assignment operator SmartPointer<T>& operator=(const SmartPointer<T>& sp) { if (this != &sp) { // Avoid self-assignment // Clean up current resources if (reference) { if (reference->Release() == 0) { delete pData; delete reference; } } // Adopt the source's resources pData = sp.pData; reference = sp.reference; reference->AddRef(); } return *this; } // Boolean negation: returns true if pointer is empty bool operator!() const { return (pData == 0); } // Equality comparison with another SmartPointer bool operator==(const SmartPointer<T>& other) const { return (reference == other.reference); } // Equality comparison with raw pointer bool operator==(const void* other) const { return (pData == static_cast<const T*>(other)); } // Inequality comparison with another SmartPointer bool operator!=(const SmartPointer<T>& other) const { return !(*this == other); } // Inequality comparison with raw pointer bool operator!=(const void* other) const { return !(*this == other); } // Friend declaration for dynamic cast template <class Y, class U> friend SmartPointer<Y> dynamic_smart_pointer_cast(const SmartPointer<U>& sp); }; // Dynamic cast for SmartPointer template <class Y, class U> SmartPointer<Y> dynamic_smart_pointer_cast(const SmartPointer<U>& sp) { SmartPointer<Y> new_sp; Y* casted_ptr = dynamic_cast<Y*>(sp.pData); if (casted_ptr != 0) { // Clean up the default reference count from new_sp if (new_sp.reference->Release() == 0) { delete new_sp.reference; } // Adopt the source's pointer and reference count new_sp.pData = casted_ptr; new_sp.reference = sp.reference; new_sp.reference->AddRef(); } return new_sp; } #endif // __SMART_POINTER_HEADER__
Notes for C++98 Compatibility
- We use
0instead ofnullptr(since C++98 doesn't supportnullptr) - The
explicitconstructor prevents accidental conversions from raw pointers toSmartPointer - The atomic operations (
__sync_add_and_fetch) are GCC extensions, which are widely supported in C98 environments where C11 toolchains aren't available
内容的提问来源于stack exchange,提问作者Nathan Owen

