VS2019编译C++自定义异常类报C2440无匹配构造函数错误
问题背景
出于兴趣编写代码时,需要实现一个支持格式化输出的自定义异常类,自行编码完成后使用VS2019编译触发错误,相关代码、编译输出、编译参数如下。
项目代码
Error.h
#pragma once #include <stdarg.h> #include <cstdio> #include <iostream> #include <stdexcept> namespace DynTyping { class Exception : public std::exception { protected: static const char* Name; const char* msg; virtual void Initialize(char* format, va_list args); public: Exception(); explicit Exception(char* format, va_list args); explicit Exception(char* format, ...); explicit Exception(const char* format, va_list args); explicit Exception(const char* format, ...); const char* what(); }; class MemoryException : public Exception { protected: static const char* Name; }; }
Error.cpp
#include "Error.h" namespace DynTyping { const char* Exception::Name = "Exception"; void Exception::Initialize(char* format, va_list args) { char msg[256]; vsprintf_s(msg, format, args); char full_msg[256]; sprintf_s(full_msg, "%s: %s", this->Name, msg); this->msg = full_msg; } Exception::Exception() : std::exception::exception() { this->msg = "Exception: an error occurred"; } Exception::Exception(char* format, va_list args) : std::exception::exception() { this->Initialize(format, args); } Exception::Exception(char* format, ...) { va_list args; va_start(args, format); this->Initialize(format, args); va_end(args); } Exception::Exception(const char* format, va_list args) : std::exception::exception() { this->Initialize((char*)format, args); } Exception::Exception(const char* format, ...) : std::exception::exception() { va_list args; va_start(args, format); this->Initialize((char*)format, args); va_end(args); } const char* Exception::what() { return this->msg; } const char* MemoryException::Name = "MemoryException"; }
Test.cpp
#include "Error.h" int main(int argc, char** argv) { using DynTyping::MemoryException; throw MemoryException("coucou"); }
编译错误输出
Build started... 1>------ Build started: Project: Test, Configuration: Debug x64 ------ 1>Error.cpp 1>Test.cpp 1>C:\Users\XXX\Documents\XXX\Prog\cpp\Test\Test\Test.cpp(7,32): error C2440: '<function-style-cast>': cannot convert from 'const char [7]' to 'DynTyping::MemoryException' 1>C:\Users\XXX\Documents\XXX\Prog\cpp\Test\Test\Test.cpp(7,32): message : No constructor could take the source type, or constructor overload resolution was ambiguous 1>Generating Code... 1>Done building project "Test.vcxproj" -- FAILED. ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
编译参数
/JMC /permissive- /ifcOutput "x64\Debug\" /GS /W3 /Zc:wchar_t /I"C:\Program Files\Python310\include" /ZI /Gm- /Od /sdl /Fd"x64\Debug\vc143.pdb" /Zc:inline /fp:precise /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /MDd /FC /Fa"x64\Debug\" /EHsc /nologo /Fo"x64\Debug\" /Fp"x64\Debug\Test.pch" /diagnostics:column
错误原因
- 直接触发编译错误的核心原因:派生类
MemoryException未继承基类构造函数。C++语法规则下,派生类不会自动继承基类的构造函数,你在MemoryException里只定义了静态成员Name,没有声明任何构造函数,也没有通过using语句引入基类构造函数,因此执行throw MemoryException("coucou")时,编译器找不到能接收const char*类型参数的MemoryException构造函数,最终报重载解析失败的C2440错误。 - 代码还存在两个隐藏的运行时问题,即便修复编译错误也会导致异常行为:
Initialize函数中将栈上局部字符数组full_msg的地址赋值给成员变量msg,函数退出后栈内存自动回收,msg会成为悬空指针,调用what()时会出现乱码,甚至触发内存访问违规崩溃。- 基类中
what()方法缺少const修饰,和std::exception基类的虚函数签名不匹配,并没有真正重写基类的虚函数,属于函数隐藏,不符合C++异常类的规范。
修复方案
按以下步骤修改代码即可解决问题:
- 在
MemoryException类中添加using声明,继承基类Exception的所有构造函数,修改后的MemoryException定义如下:class MemoryException : public Exception { protected: static const char* Name; public: using Exception::Exception; }; - 修复悬空指针问题:将成员变量
msg从const char*类型改为长度256的字符数组成员char msg[256],修改Initialize逻辑,直接把格式化后的错误信息写入成员数组,不要指向栈上临时内存,参考实现如下:// 头文件中修改msg和Initialize定义 protected: static const char* Name; char msg[256]; // 替换原const char* msg; virtual void Initialize(const char* format, va_list args); // cpp中修改Initialize实现 void Exception::Initialize(const char* format, va_list args) { char tmp[256]; vsprintf_s(tmp, format, args); sprintf_s(this->msg, "%s: %s", this->Name, tmp); } // 同时修改默认构造函数逻辑 Exception::Exception() : std::exception() { strcpy_s(this->msg, "Exception: an error occurred"); } - 修正
what()方法签名,添加const和noexcept修饰,加上override关键字确保正确重写基类虚函数:const char* what() const noexcept override { return this->msg; } - 优化const正确性:将所有构造函数、
Initialize函数中的char* format入参统一改为const char* format,去掉代码中强制把const char*转为char*的不安全写法。
内容的提问来源于stack exchange,提问作者Jerem2360
相关产品推荐
相关产品推荐

