C++易错处总结

C++真鸡儿难

自定义头文件和使用时常遇到的错误:头文件重复包含和变量重复定义

参考网址头文件重复包含和变量重复定义

多维函数做形参易错点

参考网址二维数组传参的格式

在函数中以形参的形式使用多维数组时,千万不要使用下标运算符[]
使用多维数组的正确代码例子(一共有三种方式)

#include<iostream>

using namespace std;

void function1(int ** a, size_t j)//j表示数组a的列数
{
	cout << *((int *)a + 0 * j + 0) << endl;//输出a[0][0]
}

int main()
{
	int a[2][2]{ {1,2}, {3, 4} };
	function1((int **)a, 2);//切记要用强制类型转换,不能直接写数组名称
}

在VS2017环境下,在函数中使用多维数组编译器竟然不会报错!但是程序运行时会直接崩溃!
如果在写项目时没注意,想要debug,简直是噩梦……

#include<iostream>

using namespace std;

void function1(int ** a, size_t j)//j表示数组a的列数
{
	//cout << *((int *)a + 0 * j + 0) << endl;//输出a[0][0]
	cout << a[0][0] << endl;//编译器竟然不报错!!!然而运行时程序会崩溃!!
}

int main()
{
	int a[2][2]{ {1,2}, {3, 4} };
	function1((int **)a, 2);//切记要用强制类型转换,不能直接写数组名称
}

函数返回 引用类型 或 指针类型 的易错点

函数若返回局部对象的引用或指针是严重错误行为。但关键是编译器不会报错!可以通过编译!!
若返回引用类型或指针类型并使用其返回值,则会在运行时程序崩溃……

参考资料:《C++primer》5th - P201

返回引用类型实例代码

#include <iostream>
#include <string>

using namespace std;

const string &manip()//编译器不会报错,但会运行崩溃……
{
	string ret;
	getline(cin, ret);
	cout << "  sldfjlas" << endl;
	if (!ret.empty())
		return ret;
	else
		return "Empty";
}

int main()
{
	cout << manip() << endl;//程序崩溃
}

返回指针类型程序代码

#include <iostream>
#include <string>

using namespace std;

string *function()
{
	string line;
	getline(cin, line);
	string *sp = &line;
	return sp;
}

int main()
{
	cout << *function() << endl;//使用返回的指针,程序崩溃
}

自定义类对象是const型时

当我们定义了一个类后,在函数中生成一个此类的对象,若你将生成的类对象定义为const型。
一定要注意自定义的函数访问实例域时,this指针默认为常量指针(classname *const)而不是指向常量的常量指针(const classname *const),一定要手动将函数中的this改为指向常量的常量指针。****若没注意这个细节,那么当常量对象使用类内函数(该函数使用了this指针)时,编译器就会报错。

参考资料《C++Primer》5th-P231-P232

举个栗子

#include <iostream>
#include <string>

using namespace std;

struct Student
{
	string name;

	Student() = default;
	Student(const string &name) : name(name) {}

	//void showName() const;
	void showName()
	{
		cout << name;
	}
};

//void Student::showName() const
//{
//	cout << name;
//}


int main()
{
	const Student stu1("theSunAndSnow");
	Student stu2("DriftingAE86");
	stu1.showName();//编译器报错,因为showName()使用了this指针但此this指针不是指向常量的常量指针(const Student *const)
	cout << endl;
	stu2.showName();
	cout << endl;
}

应该改为

#include <iostream>
#include <string>

using namespace std;

struct Student
{
	string name;

	Student() = default;
	Student(const string &name) : name(name) {}

	void showName() const;
	void showName()
	{
		cout << name;
	}
};

void Student::showName() const
{
	cout << name;
}


int main()
{
	const Student stu1("theSunAndSnow");
	Student stu2("DriftingAE86");
	stu1.showName();//showName()已经被定义为cosnt Student *cont的this指针
	cout << endl;
	stu2.showName();
	cout << endl;
}

typedef易错处

一件奇怪的事情是,typedef竟然不可以写在using namespace std;前面

#include <iostream>
#include <string>
#include <vector>

typedef vector<int> VI;
//std::typedef vector<int> VI;写这条语句预编译也不会通过

using namespace std;

//typedef vector<int> VI;

int main()
{
	string a, b;
	cin >> a >> b;
	VI A, B;
}

预编译会提示各种莫名奇妙的错误
必须将typedef写在using namespace std;后面

指向NULL或nullptr的空指针可以delete,不会有错误

之前一直以为空指针不能被delete,知道解决某个问题时才发现之前的认知是错误的
delete是对指针的指向空间的释放,不会改变指针的值,即指针不为NULL,把指针指向的空间释放掉,但是指针的本身内容,即指向空间的地址,是不会改变的;指针为NULL时,没有空间可释放,也就不去释放了,而指针依然有效,指针的内容依然是NULL,在指针的有效域结束时,指针本身所占内存自动被释放。参考网址

当指向一个对象的引用或指针离开作用域时对象的析构函数不会执行!

在一个作用域内new 出的动态内存在结束时一定要手动delete!否则就内存泄漏举一个错误的例子。在一个函数的作用域中new了一个string对象。

void f()
{
	std::string *p = new std::string("DriftingAE86");

	/*其他操作*/
}

常见的错误是认为函数结束后 p会被销毁。实际上,指向一个对象的引用或指针离开作用域时对象的析构函数不会执行。所以函数结束时被销毁的时p指针,但却不是p指针所指向的对象!!它依旧占据内存!!
我们来证明。用一个全局string指针sp来指向
p。在main()中调用f()之后再调用 全局指针sp。若p指向的对象被销毁那么 *sp会导致未定义行为,程序出错,若p指向的对象未被销毁,那么使用 *sp不会有任何问题。

#include <iostream>
#include <string>

std::string *sp;

void f()
{
	std::string *p = new std::string("DriftingAE86");
	sp = p;

	/*其他操作*/
}

int main()
{
	f();
	std::cout << *sp << std::endl;
}

实际输出

DriftingAE86

果然,f函数的结束并没有销毁sp所指向的对象!解决方法也很简单要么记得delete p 要么使用智能指针

若自定义类内有指针,则一定要手写析构函数。

编译器的合成析构函数只能将指针delete,不能将指针所指向的对象delete

赋值语句本身也是一个表达式,也是有返回值的,它的返回值就是赋值给变量的值,比如b=1这个语句,会把1赋值给b,但是它本身也是个表达式,这个表达式的返回值为1。

原文地址:https://www.cnblogs.com/theSunAndSnow/p/12199441.html