1.內存釋放問題
部分容器clear之後,其capacity還是不變的,並沒有釋放空間。這時候就需要用swap的trick或者C++11的shrink_to_fit來釋放空間。具體需要釋放空間的容器和注意的問題如下:
1)只有含 reserve()/capacity() 成員函數的容器才需要用swap的trick來釋放空間,而 C++ 裏只有 vector 和 string 這兩個符合條件。在 C++11 中可以直接使用 shrink_to_fit(),這個比swap好,簡單說就是更智能了,會考慮當前狀態下容器的空間和目標所需空間來決定是否釋放空間。
2)list/deque/set/map 等容器是沒有 reserve() 和 capacity() 這兩個成員函數的,因此 swap 是無用功(除非用戶代碼使用了定製的 per-object allocator)。
2.error: invalid use of non-static data member ’XXX’
翻譯過來的意思非法訪問了類裏的某些成員變量。最後發現是作用域的問題。
例如我在Test.h中聲明瞭函數:
std::unique_ptr<std::unordered_map<std::string,std::string>>& getMember();
在Test.cpp裏實現該函數:
std::unique_ptr<std::unordered_map<std::string,std::string>>& getMember() {
return Test::ptr_of_Test; //報錯 invalid use of non-static...
}
因爲,編譯器並不知道這個getMember是Test.h的,而且Test作用域裏的ptr_of_Test又不是靜態成員,所以報錯。所以實現Test.h的函數時要聲明函數作用域,正確如下:
std::unique_ptr<std::unordered_map<std::string,std::string>>& Test::getMember() { //加上Test::
return Test::ptr_of_Test; //return ptr_of_Test;也可以,因爲當前作用域已經指定
}
3.error: passing ‘const xxx’ as ‘this’ argument of xxx discards qualifiers [-fpermissive]|
例如以下代碼:
#include <bits/stdc++.h>
using namespace std;
class Test {
public:
Test () {}
map<int,int>& getmap() {
return Hash;
}
map<int,int>Hash;
};
void output(const Test& t){
auto& curHash = t.getmap(); //報錯
cout << curHash.size() << endl;
}
int main() {
Test test;
output(test);
}
出現該問題的原因是因爲我ouput的參數是一個const常量,儘管我t.getmap()得到的map我是隻讀,但編譯器不知道。因此解決辦法有3種:
1)將getmap函數申明爲常量
#include <bits/stdc++.h>
using namespace std;
class Test {
public:
Test () {}
map<int,int> getmap() const { //將函數申明爲常量
return Hash;
}
map<int,int>Hash;
};
void output(const Test& t){
map<int, int> curHash = t.getmap(); //但就實現不了變量引用
cout << curHash.size() << endl;
}
int main() {
Test test;
output(test);
}
2)不要使用const參數
使用非const參數是最方便的,但是我們又怕代碼不小心改動了此參數,聲明爲const比較安全,且理論上效率更高。
#include <bits/stdc++.h>
using namespace std;
class Test {
public:
Test () {}
map<int,int>& getmap() {
return Hash;
}
map<int,int>Hash;
};
void output(Test& t){ //使用非const參數
auto& curHash = t.getmap();
cout << curHash.size() << endl;
}
int main() {
Test test;
output(test);
}
3)放棄getmap函數
#include <bits/stdc++.h>
using namespace std;
class Test {
public:
Test () {}
map<int,int>& getmap() {
return Hash;
}
map<int,int>Hash;
};
void output(const Test& t){
auto& curHash = t.Hash; //直接使用Hash,放棄使用getmap
cout << curHash.size() << endl;
}
int main() {
Test test;
output(test);
}
以上是簡化代碼。其實我遇到的問題是:併發過程中更新模型,我用了一個buffMap數組,更新Index爲cur^1的Map,等到模型更新完畢再將Index更新爲cur^=1,這樣就避免的併發過程中的產生同時讀寫產生錯誤問題。基於此,這個模型更新過程需要引用不同index的map。所以我寫了一個getMap的函數,結果出現以上錯誤。
最終解決辦法是:我寫一個版本1)的getIndex,然後使用此Index來直接引用Public變量,也就是版本3)。
4.出現undefined的一個原因
以前打ACM都是隻有一個源文件,所有變量都來自當前文件和std的命名空間裏,對作用域的理解只停留在概念層級,現在才比較理解作用域意義。很多時候未定義的原因就是因爲沒有使用域運算符::
。經常在編譯a.cpp的時候發現某些在a.h定義的變量顯示undefined,就是因爲沒有使用域運算符。作用域問題還可能出現2.中的error。
5.override和final的一點理解
5.1 override
由於virtual關鍵字並不是強制性的,因此有時候我們在子類中重寫的時候會錯寫參數類型、const關鍵字等,override則是顯示錶明該函數是重寫父類的函數,防止出錯。例如以下錯誤:
我們原意是想重寫父類print,但由於子類print的參數是short型,所以這是重載而不是重寫。
該程序輸出的是Base::print()-> 6
而不是Child::print()-> 6
#include <bits/stdc++.h>
using namespace std;
class Base {
public:
Base() {}
virtual void print(short x) {
cout << "Base::print()-> " << x <<endl;
}
map<int,int>mp;
virtual ~Base() {}
};
class Child : public Base {
public:
Child() {}
void print(int x) {
cout << "Child::print()-> " << x << endl;
}
~Child() {}
};
int main() {
Base *base = new Child();
base->print(6);
}
但如果在子類加上override關鍵字,void print(int x) override;
。則該程序會報錯 ‘void Child::print(int)’ marked ‘override’, but does not override。這就很好的解決了錯寫的問題。
下面再舉2個例子:
1)override報錯提示、只需要再聲明時加override關鍵字
#include <bits/stdc++.h>
using namespace std;
class Base {
public:
Base() {}
virtual void update();
map<int,int>mp;
virtual ~Base();
};
class Child : public Base {
public:
Child() {}
void update() override;
~Child() {}
};
void Child::update() { // 2)
mp[2] = 2;
}
int main() {
Base *base = new Child();
base->update();
cout << base->mp.size() << endl;
delete base;
}
a)error: ’void Child::update(int)’ marked ‘override’, but does not override
錯誤顯示此函數並不是在override。
例如以下兩個錯誤代碼:
class Child : public Base {
public:
Child() {}
void update() override {} //正確
void update1() override {} //錯誤
void update(int x) override {} //錯誤
~Child() {}
};
b)error: virt-specifiers in ‘update’ not allowed outside a class definition
錯誤顯示override不允許在class外定義。
一開始在class裏定義了override方法,在class外部實現的時候還寫了override,多此一舉。
只要在class裏聲明override即可,實現並不需要加關鍵字。同理的還有virtual關鍵字。
錯誤代碼如下:
void Child::update() override{
mp[2] = 2;
}
2)只要父類是虛函數,多重繼承後此函數還是虛函數
#include <bits/stdc++.h>
using namespace std;
class Base {
public:
virtual void foo() {
cout << "Base" <<endl;
}
void bar();
};
class A : public Base {
public:
void foo() override {
cout << 'A' << endl;
}
};
class B : public A {
public:
void foo() override {
cout << 'B' << endl;
};
};
int main() {
Base *base = new B(); //輸出B
base->foo();
}
5.2 final
final關鍵字表示此類或此函數不能被繼承或重寫。我覺得通過以下這個例子的說明就能清晰理解final的作用。
#include <bits/stdc++.h>
using namespace std;
class Base {
public:
virtual void foo() {
cout << "Base" <<endl;
}
void bar();
};
class A : public Base {
public:
void foo() override final { // 提示:A::foo 被覆寫且是最終覆寫
cout << 'A' << endl;
}
void bar() final; // 錯誤:非虛函數不能被override或final
void bar() override; // 錯誤:非虛函數不能被override或final
};
class B final : public A { // 提示:類B聲明爲final
public:
void foo() override { // 錯誤:foo不能被覆寫,因爲它在A中是final
cout << 'B' << endl;
};
};
class C : public B { // 錯誤:類B爲final類,不能再被繼承
//..
};
int main() {
}
7.虛函數和抽象類一點新理解
有虛函數,其子類同名同參同關鍵字函數則是重寫,是動態多態。
沒有虛函數,其子類同名函數則是重載,是靜態多態。
例如以下重載例子,輸出的是Base
。
若給Base的foo函數加上virtual關鍵字形成虛函數,則會輸出A
。
當然這是最基礎的虛函數的意義,不是我要說的新理解,順便寫上來而已。
#include <bits/stdc++.h>
using namespace std;
class Base {
public:
void foo() {
cout << "Base" <<endl;
}
};
class A : public Base {
public:
void foo() {
cout << 'A' << endl;
}
};
int main() {
Base* base = new A();
base->foo();
}
另外的一個小積累,來自編譯過程中的一個鏈接錯誤:
error: undefined reference to `vtable for Base’.
#include <bits/stdc++.h>
using namespace std;
class Base {
public:
Base() {
cout << "BaseBuild." << endl;
mp.clear();
mp[1] = 1;
}
virtual void update(); //出錯的地方,因爲父類的update方法沒有實現
map<int,int>mp;
virtual ~Base();
};
class Child : public Base {
public:
Child() {
cout << "InheritBuild." << endl;
}
void update() override;
~Child() {
cout << "InheritDestruct." << endl;
}
};
void Child::update() {
mp[2] = 2;
}
Base:: ~Base() {
cout << "BaseDestruct." << endl;
}
int main() {
Base *base = new Child();
base->update();
cout << base->mp.size() << endl;
delete base;
}
因爲如果沒實現的話,update的虛表裏不知道內容。所以修復BUG的兩種做法:一種是實現父類的update方法,另外一種做法是如果將父類申明爲抽象類,也就是virtual void update() = 0;
抽象類不能實例化,只能由子類來實例化,即不能Base *base = new Base()。
8. 虛析構函數作用
簡單粗暴說:作用就是防止內存泄漏。
下面稍微詳細介紹下虛析構函數和普通析構函數的區別。
1)構造函數、析構函數、虛析構函數意義
構造函數:進行初始化成員變量的函數。
析構函數:在對象生命週期結束的時候,完成資源的回收和清理。如果我們在設計一個類的時候,沒有顯示聲明定義構造函數,析構函數,則編譯器會自動生成。
虛析構函數:只有當一個類被定義爲基類的時候,纔會把析構函數寫成虛析構函數。
2)爲啥使用虛析構函數?
我們創建一個基類的指針base,用完delete base時:
a. 如果基類的析構函數是虛函數,調用析構函數時會看base所賦值的對象,如果base賦值的對象是派生類的對象,就會調用派生類的析構函數,以此遞歸下去;如果base賦值的對象是基類的對象,就直接調用基類的析構函數即可,這樣不會造成內存泄露。
b. 如果基類的析構函數不是虛函數,調用析構函數時只會看指針的數據類型,而不會去看賦值的對象,這樣子類對象沒有析構,就會造成內存泄露。
3)一個看完就會清晰明瞭的例子:
#include <bits/stdc++.h>
using namespace std;
class Base {
public:
Base() {
cout << "Base construct." << endl;
}
~Base() {
cout << "Base destruct." << endl;
}
};
class Child : public Base {
public:
Child() {
cout << "Child construct." << endl;
}
~Child() {
cout << "Child destruct." << endl;
}
};
int main() {
Base* base = new Child();
delete base;
puts("##############");
base = new Base();
delete base;
}
運行結果如下:
Base construct.
Child construct.
Base destruct.
##############
Base construct.
Base destruct.
給Base類的析構函數加上virtual關鍵字(即:virtual ~Base();
)後,結果如下:
Base construct.
Child construct.
Child destruct.
Base destruct.
##############
Base construct.
Base destruct.
9. [cpplint] non-const reference
cpplint是谷歌開源的代碼規範檢查。其中遇到一個比較有意思的檢查是:”error: Is this a non-const reference? If so, make const or use a pointer. “。
cpplint 不允許函數參數爲non-const 的reference。這並不是C++標準,google的解釋是減少引用和指針之間的混淆,還進一步規定函數參數要麼是pass-by-value 要麼是const reference,而返回值只允許是pointer。也就是說,const int& a, 要麼int* a,但不可以傳int& a。
10. future/promise簡介
11. error: expected unqualified-id before numeric constant
我出現這個問題的場景是:
我在(A.h)類裏定義了const static int BUFFERSIZE = 5,然而又在別的cpp文件(B.h)裏#define BUFFERSIZE 5,而且把在A.h中include了B.h,導致重定義,出現了這個問題
解決辦法就是:去掉兩者之一
測試是否是#define衝突,可以採用條件編譯來測試。
#ifdef XXX
puts("Already define XXX");
#endif
12. error: invalid use of non-static data member
錯誤的簡化代碼:
#include <bits/stdc++.h>
using namespace std;
class Node {
const int arraySize = 5;
int num[arraySize]; //error
};
int main() {
Node node;
}
出現這個問題的原因,比較不官方的解釋:因爲一個類可以有很多對象,每個對象都有一個arraySize,而不是“唯一的”,所以必須把arraySize申明爲靜態的,或者拿到類外邊。正確代碼如下:
#include <bits/stdc++.h>
using namespace std;
const int arraySize = 5;
class Node {
int num[arraySize];
};
int main() {
Node node;
}
#include <bits/stdc++.h>
using namespace std;
class Node {
const static int arraySize = 5;
int num[arraySize];
};
int main() {
Node node;
}
13. 鎖對象lock_guard和unique_lock
14. typedef struct 和struct
以下代碼可以清晰瞭解兩者區別:
----------C語言定義結構體方式1----------
typedef struct Example { //struct Example == Foo
int a;
} Foo;
Foo foo;
struct Example foo;
----------C語言定義結構體方式2----------
typedef struct {
int a;
} Foo;
Foo foo;
----------C++語言定義結構體方式----------
struct Foo {
int a;
};
Foo foo;
15. using namespace
16. const_cast
17. 頭文件聲明順序
使用CPPLINT代碼檢查,遇到過以下錯誤:
Found C++ system header after other header. Should be: xxx.h, c system, c++ system, other
意思是:xxx.cpp的頭文件順序必須是:xxx.h、C語言頭文件、C++語言頭文件。
這就使我好奇頭文件聲明順序的作用:只是爲了規範?還是前後順序有影響程序?
答案是:會影響衝突內容,以先聲明的爲主。
測試代碼如下:
#include "math/MathFunctions.h" //順序1,裏面有自己實現的pow(double, int)函數
#include <cmath> //順序2
#include <iostream>
int main()
{
double base = 2.0;
int exponent = 3;
double result = std::pow(base, exponent);
std::cout << result << std::endl;
return 0;
}
報錯如下:
/usr/include/c++/5/cmath:418:26:
error: ‘double std::pow(double, int)’ conflicts with a previous declaration
解決辦法:
如果想使用自己MathFunctions裏定義的pow函數:把std::去掉
如果想使用std的pow函數:把cmath定義在”math/MathFunctions.h”之前
由此看出,CPPLINT規定順序是有好處的。通常情況下,先用自己定義的,然後兼容C語言的,再者C++的。
18. undefined reference to ‘dlsym’ ‘dlopen’ ‘dlclose’
undefined reference to dlsym'
dlopen’
undefined reference to
undefined reference to `dlclose’
解決辦法:
添加依賴庫 -ldl
19. cannot open shared object file: No such file or directory
- 確定是否含有此lib
- 執行ldconfig