一.自定義類型不初始化直接就賦值,比如string類會怎么樣
vectr<string>里已經給每個string對象已經分配好空間,為什么不初始化再賦值會報錯
在C++中,std::string
類是一個動態字符串類,它內部管理著一個字符數組,用于存儲字符串內容。當你創建一個std::string
對象時,如果沒有指定初始長度,編譯器默認會給它分配足夠的內存來容納后續可能添加的字符。如果你已經為std::string
對象分配了空間,但沒有初始化就直接賦值,這通常不會導致編譯錯誤,因為std::string
內部會自動處理這種情況。
然而,不初始化就賦值可能會帶來一些潛在的問題:
-
未定義行為:盡管編譯器可能允許你這樣做,但這可能導致未定義的行為。因為你沒有明確地初始化字符串,其內部狀態是不確定的,這意味著它的長度、容量以及所指向的內存可能是隨機的。在這種情況下,對字符串的操作(例如,嘗試訪問其中的字符)可能會產生不可預知的結果。
-
效率問題:雖然
std::string
會根據需要自動調整大小,但頻繁的動態調整可能會降低程序性能。每次調用push_back
、assign
等方法時,如果當前的內部緩沖區不足以容納新的字符,std::string
可能需要重新分配更大的內存區域,并復制舊的數據到新位置,這是一個相對耗時的過程。 -
內存泄漏的風險:如果你手動分配了一個字符數組(比如使用
new char[size]
),然后試圖將這個數組與std::string
關聯起來,而沒有正確地初始化std::string
,那么可能會導致內存泄漏。因為std::string
會在析構時釋放其管理的內存,但如果它不知道內存是在哪里分配的,就不能正確地執行這一操作。
為了避免這些問題,推薦的做法始終是初始化std::string
對象,即使你不提供初始值,也可以傳遞一個空字符串給構造函數:
std::string myString; // 初始化為空字符串
myString = "Hello, World!"; // 現在可以安全地賦值
或者,如果你知道字符串的預期最大長度,可以顯式指定:
std::string myString(100); // 分配100個字符的空間,但字符串長度仍為0
myString.assign("Hello, World!"); // 安全地賦值,不需要動態調整大小
這樣可以確保你的代碼更穩定且高效。
自定義類型不初始化直接就賦值,比如string類會怎么樣
如果自定義類型沒有被初始化就直接賦值,那么其成員變量的值將是不確定的,也就是未定義行為。對于string類,如果沒有被初始化就直接賦值,它的值將是隨機的,可能是一些垃圾值,也可能是一個空字符串。這種行為是不可預測的,會導致程序出現不可預期的錯誤。
建議在使用自定義類型時,一定要先進行初始化,以保證程序的正確性和可靠性。
為什么沒有被初始化就直接賦值,它的值將是隨機的
計算機中,內存是由一系列的字節構成的,當變量在內存中被定義時,它會被分配一塊內存空間。如果這個變量沒有被顯式初始化,那么這個內存空間中就會存在垃圾數據,這些數據是來自于此前使用這段內存空間的程序留下的信息,因此這個變量的值就是隨機的,可能是任何值。所以在使用變量之前必須對其進行初始化操作,否則可能會導致程序出現不可預期的錯誤。
變量的聲明和定義
(摘自)【C/C++面試必備】聲明和定義的區別
?聲明
聲明是用來告訴編譯器變量的名稱和類型,而不分配內存。
extern int var; // 聲明extern int ble =10; // 定義typedef int INT; // 聲明struct Node; // 聲明
上面代碼中,語句 extern int var;表示 var 在別的文件中已經定義,提示編譯器遇到此變量時在其它模塊中尋找其定義。語句 extern int ble = 10;表示定義了變量 ble,這一點需要注意。
注意:即使是 extern ,如果給變量賦值了,就是定義了。
定義
定義是為了給變量分配內存,可以為變量賦初值。
注意:全局變量或靜態變量初始值為0,局部變量初始化為隨機值。
在 C/C++ 中,變量的聲明和定義區別并不大,定義和聲明往往是同時發生,變量定義時,會根據變量類型分配空間,如下所示:
int value ; //聲明 + 定義struct Node { // 聲明 + 定義int left;int right;
};
變量的聲明、定義、賦值和初始化都是在預處理階段進行的嗎
二.std::vector::iterator 為什么不重載運算符>>
std::vector::iterator 是一個迭代器,用于遍歷 vector 中的元素。它不重載運算符>>,是因為這個運算符已經被用于輸入流。如果重載運算符>>,會導致代碼的可讀性變差,同時也會帶來潛在的歧義。
當我們使用輸入流時,我們通常使用運算符>>從輸入流中讀取數據。如果 std::vector::iterator 也重載了這個運算符,那么就無法確定應該調用哪個運算符。這會導致代碼的可讀性變差,并且可能會出現難以調試的問題。
因此,std::vector::iterator 沒有重載運算符>>。如果需要從輸入流中讀取 vector 中的元素,可以使用其他方式,例如循環遍歷 vector 并逐個讀取元素。
這些可以重載:++? *(前置) ==
三.iterator end() { return _finish; }可以寫成 iterator& end() { return _finish; }嗎?
引用返回的話,你改變這個迭代器的指向,那么容器中的迭代器指向也會跟著改變,就會讓容器損壞,所以需要的是一個拷貝行為。
iterator beign 和iterator _start是兩個不同的哦。
四.不能給初始化列表動態開辟空間
在 C++ 中,初始化列表用于初始化類的成員變量。如果一個類的成員變量需要動態分配內存,那么在初始化列表中是不能進行動態內存分配的。因為初始化列表是在構造函數執行之前執行的,而動態內存分配必須在程序運行時才能完成,所以無法在初始化列表中進行動態內存分配。
舉個例子,假設一個類的成員變量包含一個指向 int 類型數組的指針,如果要在構造函數中動態分配內存給這個數組,可以使用 new 操作符來實現。但是,由于初始化列表是在構造函數之前執行的,所以在初始化列表中不能使用 new 操作符來分配內存。
五.具有常性的變量不能引用
具有常性的變量(const variable)是指其值不能被修改的變量。由于其值不可更改,因此不能將其引用給一個非常量變量,因為這樣做就會打破 const 變量的常性。而將一個 const 變量引用給一個常量變量是可以的,因為常量變量的值也不會被修改。
例如,以下代碼是不合法的:
const int a = 5;
int &b = a; // 錯誤!不能將 const 變量引用給非常量變量
而以下代碼是合法的:
const int a = 5;
const int &b = a; // 正確!可以將 const 變量引用給常量變量
六.模板函數不允許自動類型轉換,但普通函數可以進行自動類型轉換
C++ 中的模板函數(函數模板)可以被用于多種不同類型的參數,但是這些參數必須在編譯時確定,因此模板函數不允許自動類型轉換。相反,普通函數可以在參數和返回值之間進行自動類型轉換。
例如,以下是一個模板函數和一個普通函數的示例:
// 模板函數
template <typename T>
T getMax(T a, T b) {return (a > b) ? a : b;
}// 普通函數
int getMax(int a, int b) {return (a > b) ? a : b;
}
當你調用 getMax(3, 5)
時,編譯器會自動推斷出使用普通函數,因為參數是整數類型。但是,當你調用 getMax(3.5, 2.8)
時,編譯器將無法確定使用哪個函數,因為參數既可以是 int
類型也可以是 double
類型。在這種情況下,你必須顯式地指定類型來調用模板函數。
七.模板滿樹的聲明和定義必須放在一起(比如:vector<T>)
(不同于一般c++函數,聲明可以在頭文件,定義可以在cpp文件。
模板函數的聲明和定義必須放在一起,否則編譯鏈接會報錯。
非要分開寫的話,需要在cpp文件中進行模板的顯式實例化,但這需要把所有用到的T都列出來。或者在頭文件末尾include 這個cpp文件,但這本質和寫在一個頭文件沒有區別,而且由于cpp文件往往沒有防止重復include的機制,鏈接時容易報錯重復定義。
參考:
類模板的定義和實現必須在一個文件嗎_c++ 接口類 實現和定義不在同一個文件
/* a.hpp */
template <typename T>
T func(T input);/* 必須也放在a.hpp */
template <typename T>
T func(T input)
{return input;
}
漢諾塔問題,青蛙跳臺,斐波那契數列
漢諾塔問題和青蛙跳臺階問題/漢諾塔和斐波那契數列的遞歸算法實現