C/C++错误集
非静态成员不能是 constexpr
在编写Qt程序的过程中,我将类的一个数据成员声明为constexpr变量,结果编译未能通过,错误提示如下:
error: non-static data member 'xxxAddr' declared 'constexpr'
当时想了好久没有明白,为什么非静态成员不是声明 'constexpr’呢?最后在这篇帖子里找到了答案:
Why can’t non-static data members be constexpr?
类的非静态数据成员是在类被实例化之后才被创建的(也就是说在运行之后类的非静态数据成员才被初始化),而’constexpr’关键字指定的变量通常在编译阶段就能够计算出结果了,显然这点不能用在非静态数据成员上。
我在vs2019上重新演示了一下:
#include <iostream>
using namespace std;
class Test {
public:
//constexpr int var = 10; //error C2178: 不能使用“constexpr”说明符声明“Test::var”
constexpr static int var = 10; //成功
};
int main()
{
Test t;
cout << t.var << endl;
return 1;
}
条件运算符第2、3个表达式类型不一致
代码如下(错误位置及提示已经在代码中标出):
#include <iostream>
using namespace std;
void test(void)
{
cout << "hello" << endl;
}
int main()
{
bool ret = false;
int a = 10;
int b = 20;
ret = (a > b) ? true : test(), false; //error C3447: 条件运算符 ?: 的 第三操作数 属于类型 "void",但 第二操作数 既不是 throw 表达式也不属于类型 "void"
return 1;
}
这里面存在两个问题:
1、没有注意条件运算符的优先级是高于逗号运算符的。误将 “ ? : ; ” 当做了一个整体。
2、没有注意条件运算符中第2,3个表达式的类型应该保持一致。
解决办法是将 ret = (a > b) ? true : test(), false;
改为 ret = (a > b) ? true : (test(), false) ;
为了帮助理解我将C++ Primer 第5版中关于条件运算符的一段介绍摘抄过来:
cond ? expr1 : expr2;
其中cond 是判断条件,expr1 和 expr2是两个类型相同或可能转换为某个公共类型的表达式。条件运算符的执行过程是:先求出 cond 的值,如果条件为真,则对 expr1 求值并返回该值,否则对 expr2 求值并返回该值。
我们将test() 和 false 的位置换一下,通过看运行结果再次思考一下优先级和条件运算符第2、3个表达式的类型问题:
#include <iostream>
using namespace std;
void test(void)
{
cout << "hello" << endl;
}
int main()
{
bool ret = false;
int a = 10;
int b = 20;
ret = (a > b) ? true : false , test(); //成功
cout << "ret =" << ret << endl;
return 1;
}
运行结果如下:
hello
ret =0
本节程序运行环境:Visual Studio Community 2019
本节内容参考:知乎
vector类内初始化 —— C++类内初始化的正确方式
在c++中vector是一个经常用到的容器。可是我居然在 vector 类型变量的初始化上栽了跟斗 😂。情况和下面代码类似:
#include<iostream>
#include <vector>
using namespace std;
class Test{
public:
vector<int> var(10, 3); //错误
};
int main()
{
Test t;
cout << t.var.at(0) << endl;
return 1;
}
错误提示如下:
错误 2 error C2059: 语法错误:“常量”
错误 3 error C2228: “.at”的左边必须有类/结构/联合
4 IntelliSense: 应输入类型说明符
5 IntelliSense: 应输入类型说明符
刚看到这个错误的时候觉得莫名其妙,这不是 vector 最常用的初始化方法吗?
在网上找到一个解释:编译器认为 vector<int> var(10, 3);
是定义了一个函数名为var,返回值类型为vector的函数,函数有两个参数但是参数的给定方式不对。具体的论证可以参考这篇博客:类中vector的初始化问题
那么如何正确在类内初始化 vector 类型变量呢?这里有两种方法:
//方式1:
vector<int> var = vector<int>(10, 2);
//方式2:
vector<int> var{ vector<int>(10, 2) };
补充:c++只能用"=" 或者 "{…}"的形式进行类内初始化,类内部初始化是先于构造函数初始化进行的,如果是对同一个变量进行初始化,构造函数初始化会覆盖类内部初始化
本节程序运行环境: Visual Studio 2013
本节内容参考:
1.类中vector的初始化问题
2.C++ Primer 第五版(C++11)
3.类成员的内部初始化
c/c++怎样检测指针是否有效
在测试软件的过程中,程序崩溃大多都是因为使用了无效指针。怎样避免产生无效指针呢?养成一个好习惯:在 free 或者 delete 一个指针后,将指针置为 0 或 nullptr。这样做是因为在free或者delete 一个指针后,该指针的值不一定为 0或 nullptr。
现在有一个任务:要求检测出一个指针是否为空,且该指针指向的内存区间是否已经被释放了,即检测该指针是否还有效(这也是我们的检测目标。关于怎样检测指针的有效性,我在网上找到了一个方法:
bool isBadPtr(void* p){
#ifdef WIN
return ISBadPtr(p,4);//在windows下判断内存的有效性函数;
#else
//在Linux下检测无效指针
int fh = open( p, 0, 0 );
int e = errno;
if ( -1 == fh && e == EFAULT ) //无效内存;
{
return true;
}
else if ( fh != -1 )
{
close( fh );
}
return false;
}
#endif
}
首先看一下 ISBadPtr() 函数。这里的 ISBadPtr() 应该是指 IsBadReadPtr()、IsBadWritePtr() 这类函数,这类函数在微软官网中有介绍(随便提一下:这类函数包含在Windows.h头文件中)。从官网的介绍中可以看出这类函数已经过时了,并且这类函数是用于验证调用进程是否有权限访问指定的内存区间的(Verifies that the calling process has read access to the specified range of memory.)。因此用这类函数检测无效指针显然达不到我们的检测目标。
我们在看Linux环境下的检测方式。该方法利用的是 Linux的系统调用 open 函数,当 open 函数的路径名(第一个参数pathname )错误时,open 会返回 -1,且置 errno 。我们先看一个例子:
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
void main()
{
int fd = -1;
int *p;
int err;
fd = open(p,0,0);
err = errno;
printf("fd = %d, err = %d\n",fd, err);
if(-1 == fd && EFAULT == err)
printf("error\n");
else
printf("true\n");
*p = 100;
}
执行结果如下:
linux@ubuntu:~/linuxSys$ ./main.out
fd = -1, err = 2
true
Segmentation fault (core dumped)
结果让我很意外,首先 open 函数检测指针的结果是:指针有效,然而给指针的对象赋值时程序就崩溃了。为什么会这样呢?在 manual 中有对 open 的错误类型有具体介绍,其中 EFAULT 是指路径名指针超出了可访问的内存空间范围,即超出访问权限(EFAULT pathname points outside your accessible address space.)。也就是说这种检测方式也只能检测进程对指针的访问权限,而不能检测出指针是否有效,这种方式也达不到我们的检测目标。
至此,还是没有找到野指针的判断方法(T_T)。大家有什么好的方法吗?
本节内容参考:
[1] C++ 跟野指针说bye bye!
[2] IsBadReadPtr function (winbase.h)
new一个含有引用类型成员的结构体或类
先看一个程序:
#include<iostream>
using namespace std;
class A {
public:
int k;
int& b;
};
int gg = 10;
int main()
{
A* a = new A;
return 1;
}
上面这个程序有什么问题吗?编译一下: error C2280: “A::A(void)”: 尝试引用已删除的函数。错误的原因就是因为A中有一个引用成员 b ,但 A 没有提供合适的构造函数来初始化 b。在 c++ 中类的引用成员必须使用构造函数的列表初始化。我们改一下程序在看一下
#include<iostream>
using namespace std;
class A {
public:
A(int dd) :b(dd)
{
cout << b << endl;
}
int k;
int& b;
};
int gg = 10;
int main()
{
A* a = new A(gg);
cout << a->b << endl;
return 1;
}
运行结果如下:
10
13439676
运行结果还是有问题,第二次打印 b 时,b的数值不对。这是因为A构造函数不对,应该改为 A(int &dd) :b(dd)
才行,即:构造函数的形参也必须是引用类型。这是因为非引用类型的形参只是实参的一个副本,在构造函数结束后形参就被销毁了。
本节程序运行环境:Microsoft Visual Studio Community 2019
本节内容参考:c++之类内定义引用成员
Qt线程
Qt 中需要被线程管理起来的类不要指定父对象。
Qt串口使用注意
对于 Qt 中的串口类(QSerialPort )和数据成员中含有 QSerialPort 的对象的类(类的组合),应该保证它们的创建和使用是在同一个线程中完成,否则容易造成串口功能错误。下面是 Qt 帮助文档中的建议:
Note: The serial port is always opened with exclusive access (that is, no other process or thread can access an already opened serial port).
同时最好不要去继承 QSerialPort 类,因为容易造成:在主线程中创建了 QSerialPor t对象在子线程中调用,或者在子线程中创建然后在主线程中调用了。
参考:https://blog.csdn.net/atgsff/article/details/120060580
C++中匿名对象
C++ 中的匿名对象是个右值,因此不能作为一个左值引用参数传递给函数。请看下面3个示例。
示例1:
#include<iostream>
using namespace std;
class Test {
public:
Test() {}
};
void printTest(Test t) {
cout << "Test" << endl;
}
int main() {
printTest(Test()); //Test()是匿名对象,是一个右值,因此可以值传递参数给 printTest 函数
return(1);
}
结果:编译通过,正常运行。
示例2:
#include<iostream>
using namespace std;
class Test {
public:
Test() {}
};
void printTest(Test &t) {
cout << "Test" << endl;
}
int main() {
printTest(Test()); //提示该行错误
return(1);
}
结果:编译不通过,错误提示,error C2664: “void printTest(Test &)”: 无法将参数 1 从“Test”转换为“Test &”
因为Test()是匿名对象,是一个右值,不能绑定到左值引用上。
示例3:
#include<iostream>
using namespace std;
class Test {
public:
Test() {}
};
void printTest(Test &&t) {
cout << "Test" << endl;
}
int main() {
printTest(Test());
return(1);
}
结果:编译通过,正常运行
因为Test()是一个右值,因此可以绑定到右值引用上。
环境:Microsoft Visual Studio Community 2019
参考:C++中的匿名对象