记录《c++ primer》的书店例子自己的一些出错和注意。 也算是c++的一些小的注意项。
以后会不断的补充到这里。因为c++实在是坑多!
1.运算符<< 和 >> 重载必须定义为非成员函数。
成员函数:定义在类的内部,是类的一部分的函数,也称为方法, 调用成员函数时做操作数必须是该类的对象
而运算符<< 和 >> 左操作数是指定的流。
所以重载<< 和 >> 操作符时将该重载函数定义为非成员函数,且是该类的友元 friend
2.类对象作为函数的参数时会被定义为引用, 传递类对象和传递已存在类型的对象不同, 传递给函数一个参数其实
传递的是那个参数的一个副本,那么传递类对象如果不用引用就会自动调用拷贝构造函数,当类很庞大时效率会底。
如果使用指针的话,需要为指针非配内存地址,而且另一个缺点就是没有引用直观,简单。
3.函数中的参数如果是const 修饰的, 那么函数也应该定义为const
class A
{
public:
friend ostream& operator<< (ostream &os, const A a);
string B( ) { return st; }
private:
string st;
};
ostream& operator<< (ostream &os, const A a)
{
os << A.B( );
return os;
}
而我在第二段代码中,调用这个B( )函数出错了
error: passing ‘const A’ as ‘this’ argument of ‘std::string A::B( )’ discards qualifiers [-fpermissive]
在operator函数中参数A是const的 , 所以在os << A.B( ); 时,由于B是非const 函数, 所以会出错。
对一个const 对象调用非const 成员函数是不被允许的, 编译器可能认为const 对象在非const 成员函数中被修改, 所以会报error
改为string B( ) const { return st; }就正确了。
补充:const 在c++ 迭代器中
const vector<int>::iterator iter; //迭代器不能改变,也就是iter
vector<int>::const_iterator iter2; //迭代器所指对象不能改变,也就是*iter
4.如果要在有const修饰的 函数中修改成员, 那么这些成员的值之前必须添加mutable(多变的)来修饰,和c语言中的关键字volatile类似。
5.当我们定于一个函数包括它的const版本和non-const版本时,会出现的代码重复以及编译时间等等问题。
因为 const 和 non-const 成员函数有着实质等价的实现,所以一个好的解决方法是:
在non-const 版本里面调用const 版本, 因为const 本来就约定不改变函数里面的对象,non-const确没有保证
一个effictive c++的例子
不优化
class TextBlock
{
public:
const char& operator[ ] (std::size_t position) const
{
...
...
return text[position];
}
char& operator[ ] (std::size_t position)
{
...
...
return text[position];
}
};
在来看看优化的
class TextBlock
{
public:
const char& operator[ ] (std::size_t position) const
{
...
...
return text[position];
}
char& operator[ ] (std::size_t position)
{
return const_cast<char&>(
static_cast<const TexrtBlock&>(*this)[position]
);
}
};
第二段代码进行了两次转型,
第一次通过const_cast 将 non-const 转型为 const.(因为如果在non-const里面调用简单的调用operator[ ]会引起无限递归)
第二次通过static_cast从const operator[ ]的返回值移除const
6. 使用++i 不使用i++
首先说下区别, 其实这两个区别肯定大家也都知道
++i :前置自增,自增,返回自增后的值
i++ :后置自增,自增,返回自增前的副本。
也就是说后置自增需要来保存自增前的自己, 当然仅仅是一个i变量无所谓, 而且编译器一般会对这种有优化
但是在c++ 中, 循环的可能是迭代器或者更加复杂的东西, 此时后置自增效率比前置自增低的效率或许就不是一点点了
不管在何时养成习惯最好。
7. 注意容器的end( ) 返回的是容器最后一个元素的下一个。
在for循环等操作中要注意, 一不小心可能会越界。
如果用下标来访问容器,那么和数组一样是从0到size( )-1。
8. do while循环记住在while末尾添加分号
do
{
}
while( );
9.返回值为void的函数只能返回空,或者返回一个函数且函数的返回值是void
返回值非void的函数结尾必须要有单独的return语句。
10.使用std::空间作用域
.h文件中不要使用空间作用域,因为每个包含的都会在运行时都会加入这个.h文件,多重包含会出现莫名其妙的错误
使用 std::cout << "hello world" << std::endl
不使用 using namespace std;
cout << "hello world" << endl;
11.const在类内
<1.在普通成员函数呢,this指针是const类型的指针,在const成员函数,this是指向const类型的const指针。
那么const成员函数当然只能在类内成员函数使用
<2. 基于成员函数是否是const,可以进行重载
<3.构造函数不能定义为const的
<4. const static int 可以使static数据成员在类定义体内初始化,和标准有关
<5..可以通过将数据成员声明为mutable来实现对该类const对象的数据成员的更改
<6. 初始化const数据成员的唯一机会实在构造函数的初始化列表中,直接在类的定义体中以及在构造函数的定义体中初始化都是不正确的。
12.static
static 修饰的成员变量只初始化一次。归所有对象共用
static 修饰的全局变量作用于限定在本文件中.
局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
程序的局部变量存在于(堆栈)中,全局变量存在于(静态区 )中,动态申请数据存在于( 堆)中。
extern全局变量(用extern修饰的变量只是说明该变量在其他地方定义,所以在其他地方一定要用明确的定义如int a,并且不能用static修饰)、static全局变量和static局部变量的生存期都是“永久”,区别只是可见域不同。extern全局变量可见区域是工程,static全局变量可见区域是文件,而static局部变量的可见区域是块。
13.在写一个FTP时出现了个问题
我把下载函数写到了类里面,创建线程函数
pthread_create( );第三个参数是线程创建后调用的函数的函数指针,但是c++类成员函数是非静态函数,必须有对象才能有函数的地址,
那么我写c++成员函数时就会报错,说是non_static function
解决方法,把下载函数设置为类的友元函数,在类里面新创建一个函数封装下载函数并且在新创建的函数里面创建线程,pthread_create函数的第三个参数为友元函数名,第四个参数为this指针。
14.c++封装,比如为什么循环1-10不用i = 1; i++而是引入迭代器这个东西呢,我感觉应该是为了抽象和封装,迭代器其实就是封装了指针,把迭代指针抽象成迭代器,那么迭代器就是一个对象了,面向对象编程的一个思维就是对象与对象之间可以相互组合,比如算法和迭代器