博文
strcpy()与strlen()、sizeof()的区别(2006-11-09 22:28:00)
摘要:char s1[6]="china" ,s2[3]="ch";
strcpy(s1,s2);
请问内存中ina部分的数据是否被覆盖??
S1的内容是ch,还是china??
S1的长度是6??
strcpy(s1,s2); strcpy函数的意思是:把字符串s2中的内容copy到s1中,连字符串结束标志也一起copy.
这样s1在内存中的存放为:ch\0
如果说s1的长度是6,那是错误的.你没有弄清strlen与sizeof的意思。
strlen函数的意思是测试字符串的字符长度,不含字符串结束标志的。
sizeof是个运算符,它的结果是字符串在内存中的所占字节大小,它要把\0算进去的。......
C/C++ 试题剖析(1)(2006-11-09 22:17:00)
摘要:试题1:
void test1()
{
char string[10];
char* str1 = "0123456789";
strcpy( string, str1 );
}
试题1字符串str1需要11个字节才能存放下(包括末尾的’\0’),而string只有10个字节的空间,strcpy会导致数组越界;
试题2:
void test2()
{
char string[10], str1[10];
int i;
for(i=0; i<10; i++)
{
str1[i] = 'a';
}
strcpy( string, str1 );
}
指出字符数组str1不能在数组内结束可以给3分;
指出strcpy(string, str1)调用使得从str1内存起复制到string内存起所复制的字节数具有不确定性可以给7分
在此基础上指出库函数strcpy工作方式的给10分;......
《高级继承》学习笔记(2006-10-22 20:52:00)
摘要:1.一个类的成员数据有可能包含另一个类的对象。C++程序员称其为外层类包容内层类。
2.私有继承不涉及继承接口,只涉及继承实现。
3.当派生类是一种基类时必须使用公有继承。
当你想把某些功能授权给另一个类而不必访问其保护型成员时一定要使用包容。
当你需要根据一个类而实现另一个类时,并且需要访问基类的保存型成员时使用私有继承。
当需要使用多个基类时,不能使用私有继承,必须使用包容。
当基类的成员不能用于派生类时,不能使用公有继承。
4.如果要把一个类的私有成员数据暴露给另一个类,那么就应把这个类声明为一个友元。
友元关系是不可以传递的,也不可以继承,友元关系也不是互通的。
友元类的声明:
friend class newclass;
友元声明可以放在类声明的任何地方。声明之后,本类的任何成员数据就完全暴露给另一个类。
也可以把另一个类的成员函数声明为该类的友元函数。......
《静态成员变量及函数》学习笔记(2006-10-21 20:38:00)
摘要:1.类的静态成员变量在所有类的实例中是共享的。它是全局数据与成员数据的折中。可以把静态成员变量看成是属于类而不是属于对象的。因此,对类的实例对象中并不会为静态成员变量留出存储空间。
一个常见的错误是忘记定义类的静态成员变量。若HowmanyCats是类Cat的静态成员变量,则其定义句为:
int Cat::HowmanyCats=0;
此外,若该静态成员变量是私有的,那么类必须为该静态成员变量提供一个公有的访问函数。对于没有对象的静态成员,则可以定义一个不属于类的全局函数来访问该成员变量。因此可以在没有对象的情况下访问类的静态成员变量。
2.静态成员函数没有this指针,因此它们不能声明为const,且静态成员函数不能访问任何非静态成员变量。
3.如同数组名,函数名是指向函数的常指针。函数指针的声明如下所示:
long (*FunPtr) (int);
第一对括号必须有,否则就不是函数指针的定义,而是一个返回指针的函数。使用函数指针就如同使用函数名一样。
FunPtr(x);
和
(*FunPtr)(x);
一样,前一种只是后一种的简化形式。
也可以把函数指针作为函数变量来传递。例如:
void Func (void ( * ) (int &, int &) , int &, int &);
可以用typedef来定义结构 void ( * ) (int &,int &)
typedef (*VPF) (int &,int &)
则 VPF可以用来代替以上结构体。
4.为创建成员函数指针,可以使用与函数指针创建相同的语法,但要包含类名和作用域运算符(::)。如:
void (Shape::*pFunc)(int ,int);
使用方法一样,只是需要一个类的对象。......
《继承》学习笔记(2006-10-17 21:04:00)
摘要:1.向已有的类添加了新功能后的类叫做原来类的派生,原来的类叫做新类的基类。通常基类有多个派生类。
派生类的声明语法:class Dog:public Mammal
基类必须最先声明。
2.成员变量有三种限定符:private(私有型)、public(公有型)、protected(保护型)。
保护型的变量不仅可以被自己的类所访问,也可以被该类所派生出的类的成员函数所访问。而私有成员变量只可以被自己类的成员函数访问。即使是派生类也不能访问他的基类的私有成员和函数。
3.创建派生类的对象时,首先调用基类的构造函数创建对象的基类部分,然后调用派生类的构造函数,创建派生类的部分构成整个派生对象。
删除派生类的对象时,首先调用派生类的析构函数,删除对象的派生部分,然后调用基类的析构函数,删除对象的基类部分。
4.派生类的构造函数调用基类的构造函数来初始化某些成员变量,若有的成员变量希望在派生类中初始化,而基类不做初始化,则可以重载基类的构造函数,即令基类只初始化部分成员变量,然后在派生类的构造函数中显式调用该重载的构造函数。(12.3.1节)
5.派生函数可以覆盖基类函数的实现,即在派生类函数中改变基类函数的实现。
当派生类用与基类成员函数相同的返回值和签名,但却用新的实现方法创建一个函数时,就称为覆盖了该方法。签名包括:函数名、参数表以及可能用到的关键字const。
规则:一旦覆盖了任一个重载方法,那么对这个方法的所有其他原基类函数均被隐藏了,如果不想让他们被隐藏,就必须把它们全部覆盖。
若仍想调用被覆盖的函数,那可以用域限定符(::),即基类::被覆盖的方法。
6.C++扩展了其多态性,允许把派生类的对象赋给指向基类的指针。如:
Mammal *pMammal=new Dog;
Dog是Mammal派生出来的一个类。这样,就可以用该指针调用Mammal类中的任何一个方法。若想调用被Dog覆盖掉的方法,则可以用虚函数来完成。
7.虚函数。在成员函数返回类型前加关键字virtual。表明该类将做为一个基类来派生其他类。
对于虚函数,并不需要在派生类中也定义为虚函数,但继续标记为虚函数是比较好的理解手段。
正确调用函数,对于6中的pMammal指针,调用基类的虚函数,若在派生类中覆盖了该函数,那么就调用覆盖函数,若没有覆盖,就调用基类的虚函数。但......
《高级函数》学习笔记(2006-10-16 21:58:00)
摘要:1.构造函数中初始化对象的成员变量。掌握这种方法。
CAT():
ItsAge(5),
ItsWeight(8)
{}
2.复制构造函数在每次复制一个对象时调用。所有的复制构造函数均有一个参数,即对同一个类的对象的引用。使这个引用成为常量最好,这样构造函数就不必改变传递进来的对象。如:
CAT(const CAT& theCat);
3.一般的复制构造函数,执行的是成员复制,简单的将成员变量复制到新的对象中,这样容易造成内存泄漏(迷途指针)。因此,复制构造函数采取开辟新的内存区来避免这种问题的发生,这也叫深层复制。如:
CAT(const CAT& rhs)
{itsAge=new int;itsWeight=new int;
*itsAge=rhs.GetAge();*itsWeight=rhs.GetWeight();}
4.运算符重载。让对象之间(其实是对象的成员变量)可以直接进行加减乘除等运算。
重载前置运算符的格式:returnType operator op;这里op是要重载的运算符(+、—等)。如
void operator ++(){++itsVal;}
在生成的对象i进行自加执行简单的命令:++i;
如果要将自加后的对象赋给其他的对象,则只需修改函数返回值的类型为类对象。
几种不同的前置运算符的声明方法,注意返回值的类型:
classType operator ++(){++itsVal;return classType(itsVal);}
const classType& operator ++(){++itsVal;returh *this;}
在效率方面,最后一个声明语句较好。
5.后置运算符的声明。同前置运算符的区别在于加入一个整型参数。如:
void operator ++(int flag){itsVal++;}
其中参数flag并不参加任何运算,只是标明是后置运算符的声明。
6.重载加号运算符。声明如下:
classType operator+ (const classType& rhs){return classType(itsVal+rhs.GetVal());}
这样可以对实例化的两个对象进行加法运算。
7.赋值运算符。声明如下:
......
《引用》学习笔记(2006-10-13 21:01:00)
摘要:1.引用就是一个别名,当声明一个引用时,就必须把它初始化为另一个对象名,也就是目标。声明格式如下:
int &rSomRef = someInt;
对引用的操作等同于对原对象的操作。
2.对引用进行取址运算,返回的值是目标的地址,因为引用只是目标的别名。
3.引用不能够被重新赋值。若对引用重新赋值就相当于对目标重新赋值。
引用只能是对对象的引用,而不能是对类或类型的引用。
引用不能为空,即不能像空指针一样被赋为零。
4.使用引用传递参数,不是在函数的作用域中创建一个拷贝,而是直接把原对象传递给函数。因此在函数中对引用的改变也会反映到函数外。
5.指针声明的函数: void swap(int *x , int *y);
引用声明的函数: void swap(int &x , int &y);
6.若要确保被传递的引用参数不被改变,则使用const指针或引用来传递。如:
const Cat& Function(const Cat& theCat);
7.在同一个参数列表中同时使用引用、指针及采用值传递是合法的。
要避免将局部的对象以引用的方式返回,因为局部对象在函数作用域之外就会被删除。
对于引用,不能使用delete运算符。
8.如果被返回的对象是局部的,那么就必须采用按值传递,否则就加返回一个不存在的对象的引用。......
《指针》学习笔记(2006-10-12 21:39:00)
摘要:1.指针是保存内存地址的变量。
int *pAge=0;
值为零的指针被称为空指针。所有指针在定义时都应该被初始化。没有初始化的指针称为失控指针。
int age=50;
pAge=&age;
以上是为指针赋值的完整过程。
2.对于变量,类型会告诉编译器需要多少存储器去装载。但对于指针,类型并不这样做,所有指针均是4个字节。
3.使用间接引用运算符(*)来引用指针。当一个指针被间接引用时,就读取其所保存的地址处的值。
4.指针有以下三种用途:
i.处理自由存储区的数据;
ii.访问类的成员数据和函数;
iii.通过引用的方式向函数传递变量。
5.局部变量和函数形参位于栈中,寄存器则用于内存管理(如保存栈顶指针和指令指针),代码区位于代码区,全局变量区,其余的内存空间作为自由存储区,称为堆。
栈在函数返回时会自动清除。自由存储区在程序结束之前不会自动清除,所以在占用之后必须主动释放。
6.C++中使用关键字new来分配自由存储区中的内存。new的返回值是内存的地址,它必须被赋给一个指针。如:
unsigned short *pPointer=new unsigned short;
当不在使用一块内存时,使用关键字delete,它的作用是释放内存。
7.内存泄漏。
指针本身是一个局部变量,当声明指针的函数返回时,指针的作用域也就结束了,因此被丢弃了。然而由new申请的内存不会自动释放,于是这块内存就不能被其他数据使用,这种情况就称为“内存泄漏”。因此要记得使用delete来释放内存。释放后最好将指针赋值为0,变为空指针。
另一种造成内存泄漏的情况是:在没有删除一个指针之前就对其重新赋值。如:
unsigned short int *pPointer=new unsigned short int;
*pPointer=72;
pPointer=new unsigned short int;
pPonter=84;
这时保存72的内存空间变得不可用,也没办法再去释放它。应该是再次使用时,先释放。
8.也可以在自由存储区内为类的对象分配内存。如:
Cat *pCat=new Cat;
这条语句调用了默认构造函数(无参数的构造函数)。删除这类指针时,在内存释放之前会调用对象的析构函数。
也就是说,内存分配是在类对象......
《程序流程》笔记(2006-10-11 21:52:00)
摘要:1.while循环语句中的测试条件句可以是任何有效的C++语句或语句块,它也可以包括使用逻辑运算符的表达式。
2.若需要在执行while循环中的所有语句之前返回到while循环的开始处,则可以用continue语句。
若需要在满足退出循环条件之前跳出循环,则可以用break语句。
3.do...while循环在执行测试条件之前先执行循环体,保证循环体至少被执行一次。
4.for循环语句的执行过程分析
for(initilization,test,action)
statement;
initilization语句用来初始化counter的值,或是为循环做好准备。test语句可以是任何C++表达式,在每次进入循环体时都要对它进行测试。如果test测试为真,则执行循环体,然后执行action语句。
4.空for循环语句。若循环体内不放任何语句,也切记要放一条空语句(;)。
5.switch表达式可以是任何C++合法的表达式,但是该表达式的值的类型只能是int、char、enum这三种类型。......
《类的定义》相关笔记(2006-10-11 21:04:00)
摘要:1.一个类可以包含各种类型的变量,也可由其他的类组成。类中的变量称为成员变量或数据成员。类中的函数称为成员函数或类的方法。
2.声明一个类并没有为该类分配内存,只有定义类的对象时,才为对象分配内存。
3.为对象赋值,而不是为类赋值。例如:
Cat是一个类,itAge是其成员变量,则以下程序不正确
Cat.itAge=5;
4.一个类的所有成员(数据和方法)默认时均为私有的。私有成员只有在类本身的方法内访问。公有成员则可以被类的所有对象访问。
5.构造函数可以带有参数,但不能有返回值,返回空值也不行。构造函数是一个与类同名的方法。构造函数创建并初始化类的对象。析构函数在对象撤除后清除并释放分配的内存。析构函数没有参数,也没有返回值。
如果没有为类创建构造函数和析构函数,编译器会自动创建一个,即为默认构造函数。没有参数的构造函数被称为默认构造函数。编译器创建的默认构造与析构函数不带任何参数,且不执行任何操作。
只要自己创建了构造函数,系统将不再提供构造函数。因此除非自己再创建默认的构造函数,否则程序将不会有默认的构造函数。
6.如果声明类方法为const,则该方法不能更改类任何一个成员变量的值。类方法声明为常量,如下所示:
void SomeFunction() const;
良好的编程习惯是将尽可能多的方法声明为const。
7.可能通过加关键字 inline 来实现成员函数的内嵌。也可以用其他类作为自定义类的成员变量。
8.C++中结构体与类相同,只是其成员默认是公有的。但结构体没有方法。
9.类的对象在内存中的大小由类的成员变量的大小的总和来决定。类方法不占用为该对象所分配的那部分内存。
10.一个类定义了两个对象,那么两个对象可以相互访问对方的数据成员,也包括私有成员。......