1.sizeof运算符
(1) 定义
sizeof是一个操作符(operator)。
其作用是返回一个对象或类型所占的内存字节数。(返回一条表达式或者一个类型名字所占的字节数。——C++ Primer)
(2) 语法
sizeof有三种语法形式:
1) sizeof (object); //sizeof (对象)
2) sizeof object; //sizeof 对象
3) sizeof (type_name); //sizeof (类型)
对象可以是各种类型的变量,以及表达式(一般sizeof不会对表达式进行计算)。
sizeof对对象求内存大小,最终都是转换为对对象的数据类型进行求值。
sizeof (表达式); //值为表达式的最终结果的数据类型的大小
(3) 数组的sizeof
数组的sizeof值等于数组所占用的内存字节数。
注意:1)当字符数组表示字符串时,其sizeof值将’/0’计算进去。
2)当数组为形参时,其sizeof值相当于指针的sizeof值。
2. 虚基类
定义:当在多条继承路径上有一个公共的基类,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明为虚基类。(百度百科)
虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class),本例中的 A 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。
3.虚函数
虚函数的作用是实现动态绑定的,也就是说程序在运行的时候动态的的选择合适的成员函数。
虚析构函数:
4.const
如果const出现在号右边,则表示指针自身是常量(顶层const);如果const出现在号左边,则表示被指物是常量(底层const);如果const出现在*号两边,则表示被指物和指针都是常量。
**顶层const(指针常量)**用来标明一个变量其本身是一个不可更改的常量。内置类型的const为顶层const。对于指针,被顶层const修改后,不可更改指针指向的对象。一个指针本身添加const限定符就是顶层const,而指针所指的对象添加const限定符就是底层const。
const int i = 1;//顶层const
int *const p = &i;//顶层const,不可更改p指向的对象
**底层const(常量指针)**用来标明一个指针或引用所指向的对象是一个不可更改常量。对于指针和引用,被底层const修改后,不可通过指针或引用修改指针指向的对象值。(可以通过其他方式修改其值)
int i = 1;
const int *p = &i;//底层const
*p = 3;//错误,不可通过被const修饰的指针修改对象值
i = 3;//正确,const指针只影响修饰的对象
执行拷贝操作时,顶层const对于拷贝操作无影响
const int i = 1;
int m = i;//i具有顶层const对于拷贝操作无影响。
但是底层const不可忽略。执行拷贝操作时,拷入与拷出对象必须具有相同的底层const,或者两对象的数据类型必须能够转换。一般来说,非常量可以转换成常量,反之不行。
int i = 1;
const int *p = &i;//正确,非常量转换为常量
int *q = p;//错误,常量不可转换为非常量
const int *r = p;//正确,等号两边都具有底层const
5.引用
引用 (& )是标识符的别名;引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。
定义一个引用时,必须同对它进行初始化使指向已存在的象。
- 把引用作为参数
int i = 17;
int& r = i;
double& s = d;
在这些声明中,& 读作引用。因此,第一个声明可以读作 “r 是一个初始化为 i 的整型引用”,第二个声明可以读作 “s 是一个初始化为 d 的 double 型引用”。
通过使用引用来替代指针,会使 C++ 程序更容易阅读和维护。C++ 函数可以返回一个引用,方式与返回一个指针类似。当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。
6.前置++和后置++的区别
从操作符重载的角度,看i++和++i的区别,是一个比较好的切入点。
class Age
{
public:
Age& operator++() //前置++
{
++i;
return *this;
}
const Age operator++(int) //后置++
{
Age tmp = *this;
++(*this); //利用前置++
return tmp;
}
Age& operator=(int i) //赋值操作
{
this->i = i;
return *this;
}
private:
int i;
};
从上述代码,我们可以看出前置++和后置++,有3点不同:
**返回类型不同:**前置++的返回类型是Age&,后置++的返回类型const Age。这意味着,前置++返回的是右值,后置++返回的是右值。
左值和右值,决定了前置++和后置++的用法。
**形参不同:**前置++没有形参,而后置++有一个int形参,但是该形参也没有被用到。很奇怪,难道有什么特殊的用意?其实也没有特殊的用意,只是为了绕过语法的限制 。
**代码不同:**前置++的实现比较简单,自增之后,将this返回即可。需要注意的是,一定要返回this。后置++的实现稍微麻烦一些。因为要返回自增之前的对象,所以先将对象拷贝一份,再进行自增,最后返回那个拷贝。
**效率不同:**如果不需要返回自增之前的值,那么前置++和后置++的计算效果都一样。但是,我们仍然应该优先使用前置++,尤其是对于用户自定义类型的自增操作。前置++的效率更高,理由是:后置++会生成临时对象。
7.左值/右值
有些变量既可以当左值右可以当右值。
左值(Lvalue) →→ Location
表示内存中可以寻址,可以给它赋值(const类型的变量例外)
右值Rvalue) →→ Read
表示可以知道它的值(例如常数)
8.单引号和双引号
单引号是字符型, 双引号是字符串型
‘a’表示是一个字符,"a"表示一个字符串相当于’a’+’\0’;
9.getline
此函数可读取整行,包括前导和嵌入的空格,并将其存储在字符串对象中。
1.使用标准输入输出操作符读写string对象。
int main()
{
string s;
cin>>s;
cout<<s<<endl;
return 0;
}
这样的话,如果输入字符串带有空格(非字符串首部、尾部),则只能输出空格前部分。
而且,读取并忽略开头所有的空白字符。
2.读入未知数目的string对象
string的输入操作符和内置类型的输入操作符一样,也会返回所读的数据流。因此,可以把输入操作作为判断条件:
int main()
{
string s;
while(cin>>s)
cout<<s<<endl;
return 0;
}
当未到达文件尾且未遇到无效输入,则执行循环体,并将读取到的字符串输出到标准输出。如果到达文件尾,则跳出while循环。
3.使用getline读取整行文本
getline(cin,strings);
getline从输入流的下一行读取,并保存读取的内容到string中,但不包括换行符。即便它是第一个字符,也会终止读入,并返回。
int main()
{
string s;
while(getline(cin,s))
cout<<s<<endl;
return 0;
}
getline(cin, inputLine);
10.读取数量不定的输入数据
#include <iostream>
int main()
{
int sum = 0, value = 0;
//读取数据直到遇到文件尾,计算所有输入的值的和
while(cin >> value)//此处是重点
{
sum += value;
}
cout << " sum is: "<< sum <<endl;
return 0;
}
while循环的判据就是表达式cin >> value,这个表达式代表从标准输入中读取下一个数,保存在value中。输入运算符返回的是其左侧的对象,即cin。故这个循环检测的实际上是cin。
当使用一个istream(即cin)对象作为条件时,其效果是检测流的状态。若流有效,没有遇到错误,那么检测成功。当遇到**文件结束符或者无效输入(**本例中无效输入为非整型)时,cin会处于无效状态,循环会停止。
11.string常用函数
12.new()
“new”是C++的一个关键字,同时也是操作符。
定义:
new 类型名T(初始化参数列表)
功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以
初值。
结果值:成功:T类型的指针,指向新分配的内存;失败:抛出异常。
释放内存操作符delete
delete 指针p
功能:释放指针p所指向的内存。p必须是new操作的返回值。
new其实就是告诉计算机开辟一段新的空间,但是和一般的声明不同的是,new开辟的空间在堆上,而一般声明的变量存放在栈上。通常来说,当在局部函数中new出一段新的空间,该段空间在局部函数调用结束后仍然能够使用,可以用来向主函数传递参数。另外需要注意的是,new的使用格式,new出来的是一段空间的首地址。所以一般需要用指针来存放这段地址。具体的代码如下:
#include <iostream>
using namespace std;
int example1()
{
//可以在new后面直接赋值
int *p = new int(3);
//也可以单独赋值
//*p = 3;
//如果不想使用指针,可以定义一个变量,在new之前用“*”表示new出来的内容
int q = *new int;
q = 1;
cout << q << endl;
return *p;
}
int* example2()
{
//当new一个数组时,同样用一个指针接住数组的首地址
int *q = new int[3];
for(int i=0; i<3; i++)
q[i] = i;
return q;
}
struct student
{
string name;
int score;
};
student* example3()
{
//这里是用一个结构体指针接住结构体数组的首地址
//对于结构体指针,个人认为目前这种赋值方法比较方便
student *stlist = new student[3]{{"abc", 90}, {"bac", 78}, {"ccd", 93}};
return stlist;
}
int main()
{
int e1 = example1();
cout <<"e1: "<< e1 << endl;
int *e2 = example2();
for(int i=0; i<3; i++)
cout << e2[i] << " ";
cout << endl;
student *st1 = example3();
for(int i=0; i<3; i++)
cout << st1[i].name << " " << st1[i].score << endl;
return 0;
}
13.C++中对象new出来和直接声明的区别
(1)首先,最直观的,new出来的对象需要使用指针接收,而直接声明的不用。例如 A* a=new A() 与A a()。
(2)new出来的对象是直接使用堆空间,而局部声明一个对象是放在栈中。
new出来的对象类似于申请空间,因此需要delete销毁,而直接声明的对象则在使用完直接销毁。
(3)new出来的对象的生命周期是具有全局性,譬如在一个函数块里new一个对象,可以将该对象的指针返回回去,该对象依旧存在。而声明的对象的生命周期只存在于声明了该对象的函数块中,如果返回该声明的对象,将会返回一个已经被销毁的对象。
(4)new对象指针用途广泛,比如作为函数返回值、函数参数等``。
14.函数参数为void和没有参数的区别
C语言中的函数在声明和定义的时候可以没有参数。众所周知,如果函数被声明和定义为void f(void);则说明该函数在调用时不能传入任何参数。而如果函数被声明和定义为void f();则说明该函数在调用时候可以传入任意参数。
如果函数无参数,那么应声明其参数为 void
C++中没有区别。
在 C++语言中声明一个这样的函数:
int function(void) { return 1; }
则进行下面的调用是不合法的:function(2);
因为在 C++中,函数参数为 void的意思是这个函数不接受任何参数。
15.const与static、const在函数前与函数后区别
const在函数前与函数后区别
在普通的非 const成员函数中,this的类型是一个指向类类型的 const指针。可以改变this所指向的值,但不能改变 this所保存的地址。
在 const成员函数中,this的类型是一个指向 const类类型对象的 const指针。既不能改变 this所指向的对象,也不能改变 this所保存的地址。
任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。
const 成员函数的声明看起来怪怪的:const关键字只能放在函数声明的尾部,大概是因为其它地方都已经被占用了。
关于Const函数的几点规则:
const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数.
const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的.
const成员函数不可以修改对象的数据,不管对象是否具有const性质.它在编译时,以是否修改成员数据为依据,进行检查.
然而加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的
举例:
1、int GetY() const;
2、const int * GetPosition();
对于1
该函数为只读函数,不允许修改其中的数据成员的值。
对于2
修饰的是返回值,表示返回的是指针所指向值是常量。
static和const的区别
能不能同时用static和const修饰类的成员函数?
不可以。
解释:1:C++编译器在实现const的成员函数时为了确保不能通过该函数修改对象的状态,会在函数中添加一个隐式的参数 this指针(类名 const* const this)。一个成员函数为static的时候,该函数是没有this指针的。此时const的用法和static是冲突的。
解释2:语意矛盾。static的作用是表示该函数只作用在类型的静态变量上,与类的实例无关;const的作用是确保函数不能修改类的实例的状态,与类型的静态变量无关。因此不能同时使用。
16.void *memset(void *s, int ch, size_t n);
memset是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。
函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。
memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法 。
memset()函数原型是extern void *memset(void *buffer, int c, int count) buffer:为指针或是数组,c:是赋给buffer的值,count:是buffer的长度.
17.数组声名后不初始化,数组里的值都是0吗?
1、全局/静态数组
如果申明的是全局/静态数组,系统会把数组的内容自动初始化为0。
2、局部数组
如果申明的是局部数组,数组的内容会是随机的,不一定是0。如函数内声明:
int Func()
{
char szTest[10]; //此时内容是随机的
memset(szTest, 0, sizeof(szTest));
}
3、成员数据
如果申明的是类的成员数组,数组的内容是随机的,不一定是0。一般在类的构造函数内用memset初始化为0。