1.若用双引号表示字符串,其末尾隐藏一个字符串结束符 '\0':`#include
#include
#include
int main(){
char s1[10]={'a','b','c'};
char s2[10]={'a','b','c','\0'};
char s3[10]="abc";
char s4[10]="abc\0";
char s5[]="abc";
char s6[]="abc\0";
char s7[3]={'a','b','c'};
char s8[]={'a','b','c','\0'};
int l1,l2,l3,l4,l5,l6,l7,l8;
int si1,si2,si3,si4,si5,si6,si7,si8;
l1=strlen(s1);//3
l2=strlen(s2);//3
l3=strlen(s3);//3
l4=strlen(s4);//3
l5=strlen(s5);//3
l6=strlen(s3);//3
l7=strlen(s4);//3
l8=strlen(s5);//3
si1=sizeof(s1);//10
si2=sizeof(s2);//10
si3=sizeof(s3);//10
si4=sizeof(s4);//10
si5=sizeof(s5);//4
si6=sizeof(s6);//5
si7=sizeof(s7);//3
si8=sizeof(s8);//4
printf("%d,%d,%d,%d,%d,%d,%d,%d\n",l1,l2,l3,l4,l5,l6,l7,l8);
printf("%d,%d,%d,%d,%d,%d,%d,%d\n",si1,si2,si3,si4,si5,si6,si7,si8);
return 0;
}`
(1)引用仅是变量的别名,而不是实实在在地定义了一个变量,因此引用本身并不占用内存,而是和目标变量共同指向目标变量的内存地址,即共用内存
(2)引用和目标变量的地址是一样的,对引用的操作与对变量直接操作完全一样,即对引用的修改就是对目标变量的修改
(3)表达式中的取地址符&不再是取变量的地址,而是用来表示该变量是引用类型的变量。
(4)定义一个引用时,必须对其初始化,即 引用了谁
(5)使用一般变量传递函数的参数,即值传递,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数
(6)使用指针作为函数的参数在被调函数中同样要给形参分配存储单元,调用时必须用变量的地址作为实参
(7)使用引用传递函数的参数,在内存中并没有产生实参的副本
int main()
{
int intOne;
int &rSomeRef = intOne;
intOne = 5;
cout << "intOne:" << intOne << endl;//5
cout << "rSomeRef:" << rSomeRef << endl;//5
int intTwo = 8;
rSomeRef = intTwo;
cout << "nintOne:" << intOne << endl;//8
cout << "intTwo:" << intTwo << endl;//8
cout << "rSomeRef:" << rSomeRef << endl;//8
return 0;
}
①从现象上看,指针在运行时可改变其所指向的值,而引用一旦和某个对象绑定后就不再改变。这句话可以这样理解:指针可以被重新赋值以指向另一个不同的对象,但是引用总指向在初始化时被指定的对象,以后不能改变,但是指定的对象内容可以改变。
②从内存上分配看,程序为指针变量分配内存区域,而不用为引用分配内存区域,引用声明时必须初始化,从而指向一个已经存在的对象,引用不能指向空值。
③从编译上看,程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值是指针变量的地址值,而引用在符号表上对应的地址值是引用对象的地址值。符号表生成后就不会再改变,指针可以改变指向的对象(指针变量的值可以改),而引用对象不能改,这是使用指针不安全而使用引用安全的主要原因。
引用和指针都可以指向一个对象,都可以作为形参,都可以通过该参数修改主调函数中的变量以达到变量双向传递的目的,都可以避免值复制的发生从而减少函数调用时数据传递的开销。
如果仅使用预先定义好的cout,cin,clog对象,就不需要构造一个输出流对象,如果使用文件流将信息输出到文件,或者从文件输入到程序中,便需要使用构造函数来建立流对象。
(1)ofstream mf;mf.open(“filename”);定义一个静态文件输出流对象,然后打开文件,使流对象与文件建立联系。
(2)ofstream mf(“filename”);在调用构造函数时指定文件名。
(3)可以使用一个流先后打开不同的文件,但在同一时刻只能有一个文件是打开的。
ofstream mf;
mf.open(“file1”);
mf.close();
…
mf.open(“filen”);
mf.close();
(1)ifstream mf;
mf.open(“filename”);
(2)ifstream mf(“filename”);
它的数据成员值在对象的整个生存期间内不能改变,也就是说常对象必须初始化而且不能被更新。常对象只能调用它的常成员函数,而不能调用其它的成员函数。
无论是否通过常对象调用常成员函数,在常成员函数调用期间内,目的对象都被视为常对象,因此常成员函数不能更新目的对象的数据成员,也不能针对目的对象调用类中没有用const修饰的成员函数。
任何函数中都不能修改常数据成员,构造函数智能通过初始化列表对该数据成员进行初始化。如类A中的常数据成员a初始化:A::A(int i):a(i){}
一个常引用,无论是绑定到一个普通的对象还是常对象,通过该引用访问该对象时,都只能把该对象当做常对象。这意味着,对于基本数据类型的引用,不能为数据赋值,对于类类型的引用,则不能修改它的数据成员,也不能调用它的非const的成员函数。
运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用域不同类型的数据时导致不同的行为。
`返回类型 operator 运算符(形参表){
函数体
}`
左操作数是对象本身的数据,由this指针指出,右操作数则需要通过运算符重载函数的参数表来传递;如果是单目运算符,操作数由对象的this指针给出,不需要任何形参。
语法规定,前置单目运算符重载为成员函数时没有形参,而后置单目运算符重载为成员函数时需要有一个类型名int,此类型名仅仅是为了区分前置和后置。
在形参表中形参从左到右的顺序就是运算符操作数的顺序,如果需要访问运算符参数对象的私有成员,可以将该函数声明为类的友元函数。
vector是C++标准库提供的被封装的动态数组,它具有各种类型,它是一个类模板。定义形式:
vector <元素类型> 数组对象名(数组长度)
vector定义的数组对象的所有元素都会被初始化,所以保证类类型要有默认构造函数,所有元素初始化相同的值。
这两个都是类,c++中有类的概念为什么还保留这两个概念,因为是为了保持和c程序的兼容性。联合体是特殊形态的类,全部数据成员共享同一组单元;结构体和类唯一的区别是具有不同的默认访问控制属性。
值调用是指当发生函数调用时,给形参分配内存空间,并用实参来初始化形参(直接将实参的值传递给形参)。这一过程是参数值的单向传递过程,一旦形参获得了值便与实参脱离关系,此后无论形参发生了怎样的改变,都不会影响到实参。引用调用将引用作为形参,在执行主调函数中的调用语句时,系统自动用实参来初始化形参。这样形参就成为实参的一个别名,对形参的任何操作也就直接作用于实参。
不必一致,所有的参数是根据位置和类型而不是名字来区分的。
protected 用来声明保护类型的成员,保护类型的性质和私有类型的性质相似,其差别在于继承和派生时派生类的成员函数可以访问基类的保护成员。
#include
class datatype{
enum{
character,
integer,
floating_point
} vartype;
union
{
char c;
int i;
float f;
};
public:
datatype(char ch) {
vartype = character;
c = ch;
}
datatype(int ii) {
vartype = integer;
i = ii;
}
datatype(float ff) {
vartype = floating_point;
f = ff;
}
void print();
};
void datatype::print() {
switch (vartype) {
case character:
cout << "字符型: " << c << endl;
break;
case integer:
cout << "整型: " << i << endl;
break;
case floating_point:
cout << "浮点型: " << f << endl;
break;
}
}
void main() {
datatype A('c'), B(12), C(1.44F);
A.print();
B.print();
C.print();
}
结果:
字符型: c
整型: 12
浮点型: 1.44
可见性是标识符是否可以引用的问题;可见性的一般规则是:标识符要声明在前,引用在后,在同一作用域中,不能声明同名的标识符。对于在不同的作用域声明的标识符,遵循的原则是:若有两个或多个具有包含关系的作用域,外层声明的标识符如果在内层没有声明同名标识符时仍可见,如果内层声明了同名标识符则外层标识符不可见。
对于类的普通数据成员,每个类的对象都有一个拷贝,就是说每个对象的同名数据成员可以分别存储不同的数值,但是类的静态数据成员,每个类只有一个拷贝,由所有该类的对象共同维护和使用,这就实现了同一个类中的不同对象的数据共享。类的静态成员函数有两个方面的好处:一是它只能直接访问同一个类的静态数据成员,可以保证不对该类的其它数据成员造成负面影响;而是同一个类只维护一个静态成员函数的拷贝,节约了系统的开销,提高程序的运行效率。静态成员变量可以为私有。
#include “fn1.h”
int n
void main(){
n=20;
fn1();
cout<<"n的值为"<
}
//fn1.h文件
extern int n;
void fn1(){
n=30;
}`
结果:
n的值为30;
#include
void fn1(){
static int n=0;
n++;
cout<<"n的值为:"<<n<<endl;
}
void main(){
for(int i=0;i<10;i++){
fn1();
}
}
这个类模板的每一个实例类都会产生一个相应的静态变量。
#include
#include
void reverse(char *s,char *t){
char c;
if(s<t){
c=*s;
*s=*t;
*t=c;
reverse(++s,--t);
}
}
void reverse(char *s){
reverse(s,s+strlen(s)-1);
}
int main(){
char str[]="abcdefghijk";
cout<<"原来字符串:"<<str<<endl;//abcdefghijk
reverse(str);
cout<<"转换后字符串:"<<str<<endl;//kjihgfedcba
return 0;
}
派生类构造函数执行的一般次序为:调用基类构造函数,调用成员对象的构造函数,派生类的构造函数体中的内容。
调用方法: A::fn1();
fn2();
虚函数是动态绑定的基础,必须是非静态的成员函数,声明如下:
virtual 函数类型 函数名(形参表)
虚函数声明只能出现在类定义中的函数原型声明中,而不能在成员函数实现的时候。运行中的多态需要满足三个条件:第一是类之间满足赋值兼容规则,第二是要声明虚函数,第三是要由成员函数来调用或者是通过指针、引用来访问虚函数。
纯虚函数声明如下:
virtual 函数类型 函数名(参数表)=0;
实际上,它与一般虚函数成员的原型在书写格式上的不同就在于后面加了“=0”。声明为纯虚函数之后,基类中就可以不再给出函数的实现部分,而对于虚函数,基类中可以给出函数的实现部分。
带有纯虚函数的类就是抽象类,抽象类的主要作用是通过它为一个类族建立一个公共的接口。抽象类不能实例化,即不能定义对象。
类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代。通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员。这样,公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。
class B {...} class D:public B {...} B b1,*pb1; D d1;
(1)派生类对象可以隐含转换为基类对象,即用派生类对象中从基类继承来的成员,逐个赋值给基类对象的成员:
b1=d1
(2)派生类对象也可以初始化基类对象的引用:
B &rb=d1
(3)派生类对象的地址也可以隐含转换为指向基类的指针:
pb1=&d1