C 語言允許省略回傳型別 (預設 int),C++ 雖嚴格但編譯器不一定報警。為了可讀性、規範性與自文件化,所有函數必須明確宣告回傳型別:
// 錯誤範例
add(int a, int b) { return a + b; } // 隱式 int
// 正確範例
int add(int a, int b) { return a + b; }
void printSum(int a, int b) { std::cout << a + b; }
// C++ 標準寫法
int getValue(void) { return 42; }
getValue(); // 合法
getValue(1); // 編譯錯誤
// 錯誤寫法
int getValue() { return 42; } // C++ 中等同 void,但不明確
C 語言中 () 表示「未指定參數」,(void) 才是「無參數」。C++ 中兩者等價,但明確寫 void 更清晰。
void* pvoid;
pvoid++; // ANSI: 錯誤
pvoid += 1; // ANSI: 錯誤
理由:算術運算需要知道型別大小 (sizeof(T)),void 無大小資訊。
pvoid++; // GNU: 合法,位址 +1
pvoid += 1; // GNU: 合法
// 明確轉換為 byte 指標運算
static_cast(pvoid)++; // ANSI + GNU 皆合法
static_cast(pvoid) += 1; // 現代 C++ 推薦
實務建議:盡可能避免 void* 算術,改用模板或具體型別指標。
記憶體操作、序列化、泛型容器等「只關心位元組、不關心型別」的場景:
// 標準庫範例
void* memcpy(void* dest, const void* src, size_t n);
void* memset(void* ptr, int value, size_t n);
// 實際呼叫
int arr1[100], arr2[100];
memcpy(arr1, arr2, sizeof(arr1)); // int* 隱式轉 void*
memset(arr1, 0, sizeof(arr1));
若改為 char* 參數,呼叫端需強制轉換,破壞介面抽象層次。
void a; // 錯誤:無法建立 void 變數
void func(void a); // 錯誤:參數不能是 void
void 體現抽象概念,如同抽象基類無法實例化。所有具體變數皆有具體型別。
#include
#include
class Buffer {
std::unique_ptr data_;
size_t size_;
public:
Buffer(size_t n) : data_(std::make_unique(n)), size_(n) {}
// 規範四:接受任意資料指標
void write(const void* src, size_t offset, size_t n) {
if (offset + n > size_) throw std::out_of_range("Buffer overflow");
std::memcpy(data_.get() + offset, src, n); // 規範三:用 uint8_t* 運算
}
void read(void* dest, size_t offset, size_t n) const {
if (offset + n > size_) throw std::out_of_range("Buffer overflow");
std::memcpy(dest, data_.get() + offset, n);
}
// 規範一:無回傳值明確聲明
void clear() { std::memset(data_.get(), 0, size_); }
// 規範二:無參數明確聲明
size_t capacity(void) const { return size_; }
};
| 場景 | 傳統 void* | 現代替代 |
|---|---|---|
| 泛型緩衝區 | void* data | std::span (C++20) |
| 任意型別儲存 | void* ptr | std::any / std::variant |
| 型別擦除 | void* + 函數指標 | std::function / 概念 |
| C API 互操作 | void* context | 封裝於 RAII 類別內部 |