说明
本文主要记录了我在开发过程中遇到的问题,包括编译错误、调试失败等的总结。
我会给出问题的原因及解决方法,并尽可能列出可以提供详细信息的相关链接。
可以在本页搜索关键字获取相关内容。
注:发生问题的原因可能不止一种,这里的解决方法并非唯一和最优,欢迎探讨。此文会持续更新。
环境
gcc及系统版本:
% gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% uname -a
Linux 4.4.0-94-generic #117-Ubuntu SMP Tue Aug 29 08:13:56 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
编码经验
-
使用c11的for(auto x : vec)循环修改元素时,容器中实际值未改变
- 原因:该循环以拷贝值的方式访问容器,修改的是临时变量的值,原值未改变
- 修改:使用 for(auto &x : vec)即可访问修改原始值。当不需要修改原始内容时,建议使用 for (auto const &x : vec),该方式直接访问原始内容,省略拷贝过程
-
调试时打印输出函数名、文件名、行号等信息
__FUNCTION__, __FILE__, __LINE__
- 有没有跨平台的sleep函数
// c11
#include <thread>
#include <chrono>
std::this_thread::sleep_for(std::chrono::seconds(1));
-
boost asio async_accept没有响应
- 原因:未启动io_service或者启动位置不对
- 修改:在调用异步操作之后启动ios.run()(注意在一个线程内启动,否则会阻塞)
-
boost asio中已经post的任务未执行就退出了
- 原因:post后执行了ios.stop(),导致工作线程退出,任务不再执行
- 修改:使用
boost::shared_ptr<boost::asio::io_service::work> m_work
绑定ios,执行m_work.reset()会等待任务执行完成后退出
-
boost asio中,使用
m_socket.is_open();
总是返回true-
原因:在TCP连接中,客户端和服务端建立连接后,再使用该函数测试连接状态是无效的,因为底层连接可能已经断开连接,而应用层无从得知
-
修改:可以再次读写该socket,返回正常,则连接,异常则已经断开
-
-
g++的编译选项中,-std=c++0x 和 -std=c++11 应该选择哪一个,有什么区别?
-
如果编译器支持,应该选用 -std=c++11
-
仅对于不支持-std = c ++ 11的较旧的编译器版本,才需要旧的-std = c ++ 0x。0x可能代表了03、08等发布的尚不稳定的特性,C ++ 11标准正式发布之前更改了一些细节,以适应该标准不断变化的工作草案
-
关于兼容性:可能存在差异和不兼容性,但是这些不仅仅与-std = c ++ 0x的使用有关,还与编译器的特定版本有关。 当编译器同时支持两者时,它们应该相同
-
-
经常会需要删除map中的某个迭代器,有没有好用的方法?
- 当需要删除map中指定迭代器时,一般都是先遍历map,找到后再删除。在for循环中使用iter++,再循环体中mymap.erase(iter)的错误做法应该避免
- 注意到迭代器在删除后失效,for循环不再会退出后,下面是行之有效的代码:
std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
if (ShouldDelete(*itr)) {
std::map<K, V>::iterator toErase = itr;
++itr;
myMap.erase(toErase);
// 下面这一行与上面三行等效
//myMap.erase(itr++); // <--- Note the post-increment!
} else {
++itr;
}
}
// 下面适用于c11
std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
if (ShouldDelete(*itr)) {
itr = myMap.erase(itr); // erase会返回指向下一个元素的迭代器
} else {
++itr;
}
}
-
新建对象时的两种方式:new Foo() vs new Foo
- 两种方式都可以创建对象,但是否带小括号会影响创建对象的具体结果:是否初始化。初始化的方式又取决于编译器的版本及该对象是否是POD类型。
- POD类型(Plain Old Data),简单的解释就是没有构造、析构函数且没有虚函数的类,如
struct A { int m; }; // POD
- new Foo(): 显式构造,new Foo:是否调用构造函数不重要。对于自定义构造函数的类来说,没有区别。
- 具体区别如下Do the parentheses after the type name make a difference with new?:
struct A { int m; }; // POD
struct B { ~B(); int m; }; // non-POD, compiler generated default ctor
struct C { C() : m() {}; ~C(); int m; }; // non-POD, default-initialising m
// c++98
// new A: 未确定的值
// new A(): 0初始化
// new B: 默认构造,m未初始化
// new B(): 默认构造,m未初始化
// new C: 默认构造,0初始化
// new C(): 默认构造,0初始化
// c++03
// new A: 未确定的值
// new A(): 值初始化,因为是POD,所以是0初始化
// new B: 默认初始化,默认构造,m未初始化
// new B(): 值初始化,因为是默认构造,0初始化
// new C: 默认初始化,调用默认构造
// new C(): 值初始化,调用默认构造
-
如何把std::unique_ptr放入map?
- std::unique_ptr 是只移型别,不能复制,移动后源指针会被置空
- 可以使用
myMap.insert(std::make_pair(0, std::unique_ptr<Class1>(new Class1())));
或者myMap[0] = std::unique_ptr<Class1>(new Class1());
,也可以myMap.insert(std::make_pair(0,std::move(up)));
- 如果支持c14,建议使用
myMap[0] = std::make_unique<Class1>();
-
如何只使用c11使线程进入永久休眠?
- 经常需要在主线程中休眠,以等待其他线程处理完成
- 有多种方式可以达到这个目的,如
while(true) usleep(1)
,while(1) pause()
等,其实sleep方式也会耗费cpu时间(极短) - 也可以使用一个永远不会返回true的信号量,如:
// 可以达到目的,但非常无聊,且毫无理由如此做
std::condition_variable cv;
std::mutex m;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, []{return false;});
- 也可以使用一个永远不会置位的期值:
std::promise<void>().get_future().wait();
,同样没有理由这样做 - 好了,较优雅的方式来了:
std::this_thread::sleep_until(std::chrono::system_clock::now() + std::chrono::hours(std::numeric_limits<int>::max())); // sleep到天荒地老
- 参考:https://stackoverflow.com/questions/36711131/how-to-sleep-forever-only-using-c11
编译错误
-
invalid new-expression of abstract class type
-
原因:实例化了一个虚类,该类至少有一个成员函数是虚的,未实现。
-
修改:
-
[] 实现该成员函数
-
[] 从该类派生一个类,实现各成员,并使用该派生类
-
-
‘[Class name]’ does not name a type in C++
-
原因:两个h文件嵌套引用,在某个文件中直接使用另一个文件中定义的类
-
修改:在使用前声明该类:
Class A
-
-
error: ‘shared_ptr’ in namespace ‘std’ does not name a template type
- 原因:没有包含头文件
- 修改:
- 如果使用
std::shared_ptr
,使用#include <memory>
- 如果使用
boost::shared_ptr
,使用#include <boost/shared_ptr.hpp>
-
error: declaration of ‘auto data’ shadows a parameter
- 原因:该行代码中的变量data已经在之前定义过,它覆盖了参数,也就是说该变量与函数参数同名
- 修改:修改该变量名不与参数同名即可
-
error: invalid use of incomplete type 'class ClientMgrBase
- 原因:使用了不完整的类型的类,仅前向声明了一个类,而未见完整定义时就使用该类的指针
- 修改:把类的声明放在h中,在cpp中使用该类的指针
-
error: jump to case label [-fpermissive]
- 原因:在某个case中定义了局部变量,且之后仍有其他case语句,所以该变量的作用域一直有效。这样可能会导致异常或崩溃
- 修改:在每个case后使用作用域限定符{},或者不要在case中定义局部变量
-
error: tr1::bad_weak_ptr
- 原因:把shared_form_this()用在了一个没有shared_ptr指向的对象上,enable_share_from_this在实现时使用了一个对象的weak_ptr,而这个weak_ptr需要对象的shared_ptr进行初始化。由于此时对象尚未构造完成,所以会抛出std::bad_weak_ptr的异常
- 修改:使用
boost::shared_ptr<A>(this)
创建对象A的智能指针作为参数,或者在Init函数中完成类似指针的使用 - enable_shared_from_this - empty internal weak pointer?
-
initial value of reference to non-const must be an lvalue
- 原因:传递了一个非const引用的指针
- 修改:声明为const,或者把指针赋值给变量,再传递该变量
- C++ initial value of reference to non-const must be an lvalue
-
Undefined reference to ‘dlsym’
- 原因:程序中使用了 dlsym 系列函数,如进行了动态库的动态加载等,而没有链接该库
- 修改:
g++ demo.c -ldl
,如果还是不行,尝试使用-lstdc++ -Wl,--no-as-needed -ldl
- Undefined reference to ‘dlsym’
-
‘memset’ was not declared in this scope
- 原因:没有包含相关的头文件
- 修改:
#include <cstring>
-
使用类的静态成员变量时,undefined reference to `A::m_a’
- 原因:只在头文件中声明了静态成员变量,未定义该变量,所以未为该变量分配存储空间(注意,如果是非静态的,会在实例化时分配内存,不会报错)
- 修改:在使用之前定义该成员变量即可。在cpp中,使用它之前定义,如:
int A::m_a = 0;