part 0 重要知识点
- 命名空间
- 面向对象编程思想
- 类的封装
- 构造与析构
- 静态成员
- this 指针
- 友元函数与友元类
- 运算符重载
- 继承与派生
- 虚函数与多态
- 函数模板与类模板
- 输入输出流
- 异常处理
part 1 Basis
定义一个新的string类
实现构造函数、析构函数、拷贝构造函数、赋值函数
class NewString{
public:
NewString(const char*str=nullptr);//构造函数
NewString(const NewString& other);//拷贝构造函数
NewString& operator=(const NewString& other); //赋值函数
~NewString(); //析构函数
void show();
private:
char *m_data;
};
NewString::NewString(const char *str){
if(str==nullptr){
m_data = new char[1];
*m_data = '\0';
}
else{
size_t len = strlen(str);
m_data = new char[len+1];
strcpy(m_data,str);
}
}
NewString::NewString(const NewString &other){
if(m_data) delete [] m_data;
size_t len = strlen(other.m_data);
m_data = new char[len+1];
strcpy(m_data,other.m_data);
}
NewString & NewString::operator=(const NewString &other){
if(this==&other) return *this;
size_t len = strlen(other.m_data);
m_data = new char[len+1];
strcpy(m_data,other.m_data);
return *this;
}
NewString::~NewString() { delete []m_data;}
编写一个标准的strcpy函数
char *NewStrcpy(char *strDest,const char*strSrc){
assert((strDest!=NULL)&&(strSrc!=NULL));
char *address = strDest;
while((*strDest++ = *strSrc++)!='\0');
return address;
}
atoi
int myAtoi(const char* pstr)
{
// char* tmp = new char[strlen(pstr)];
// char* tmp = const_cast<char*>(pstr);
char* tmp = (char*)malloc(sizeof(char) * strlen(pstr));
strcpy(tmp, pstr);
int ans = 0;
while(*tmp == ' ') tmp ++; // wipe out spacings
bool negative = false;
if (*tmp == '-') // positive? negative?
{
negative = true;
tmp ++;
}
while(*tmp != '\0' && *tmp >= '0' && *tmp <= '9')
{
if(negative==false && ans>=INT_MAX)return INT_MAX;
if(negative==true && ans*(-1)<=INT_MIN)return INT_MIN;
ans = ans * 10 + ((*tmp) - '0');
tmp ++;
}
if(negative) return ans * (-1);
else return ans;
}
分别写出BOOL,int,float,指针类型的变量a 与“零”的比较语句。
//BOOL
if ( !a ) or if(a)
//int
if ( a == 0)
//float
const EXPRESSION EXP = 0.000001
if ( a < EXP && a >-EXP)
//pointer
if ( a != NULL) or if(a == NULL)
用变量a给出下面的定义
a) 一个整型数(An integer)
b) 一个指向整型数的指针(A pointer to an integer)
c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer)
d) 一个有10个整型数的数组(An array of 10 integers)
e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)
f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers)
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)
答:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
翻转链表
struct NODE{
NODE* next;
int number;
};
typedef struct NODE Node;
Node *ReverseList(Node *head)
{
if(head==NULL||head->next==NULL)return head;
Node *p1 = head;
Node *p2 = head->next;
Node *p3 = head->next->next;
p1->next = NULL;
while(p3)
{
p2->next = p1;
p1 = p2;
p2 = p3;
p3 = p3->next;
}
p2->next = p1;
head = p2;
return head;
}
合并两个有序链表(递归)
Node *MergeRecursive(Node *head1,Node *head2)
{
if(head1==NULL)return head2;
if(head2==NULL)return head1;
Node *head = NULL;
if(head1->number < head2->number)
{
head = head1;
head->next = MergeRecursive(head1->next, head2);
}
else
{
head = head2;
head->next = MergeRecursive(head1, head2->next);
}
return head;
}
字符串循环右移
void LoopMove(char *pStr,int steps)
{
int MAX_LEN = 100;
size_t n =strlen(pStr) - steps;
char tmp[MAX_LEN];
strcpy(tmp,pStr+n);
strcpy(tmp+steps,pStr);
*(tmp+strlen(pStr)) ='\0';
strcpy(pStr,tmp);
}
int main()
{
char c2[]="happy";
cout<<c2<<" "<<strlen(c2)<<endl;
LoopMove(c2, 2);
cout<<c2<<endl;
return 0;
}
part2 简述题
将“引用”作为函数参数有哪些特点?
(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用”*指针变量名”的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
const 与 #define 的比较 ,const有什么优点?
(1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。
(2) 有些集成化的调试工具可以对 const 常量进行调试,但是不能对宏常量进行调试。
宏定义 #define 和常量 const 的区别 类型和安全检查不同
宏定义是字符替换,没有数据类型的区别,同时这种替换没有类型安全检查,可能产生边际效应等错误;
const常量是常量的声明,有类型区别,需要在编译阶段进行类型检查
编译器处理不同
宏定义是一个”编译时”概念,在预处理阶段展开,不能对宏定义进行调试,生命周期结束与编译时期;
const常量是一个”运行时”概念,在程序运行使用,类似于一个只读行数据
存储方式不同
宏定义是直接替换,不会分配内存,存储与程序的代码段中;
const常量需要进行内存分配,存储与程序的数据段中
定义域不同
void f1 ()
{
#define N 12
const int n 12;
}
void f2 ()
{
cout<<N <<endl; //正确,N已经定义过,不受定义域限制
cout<<n <<endl; //错误,n定义域只在f1函数中
}
定义后能否取消
宏定义可以通过#undef来使之前的宏定义失效
const常量定义后将在定义域内永久有效
void f1()
{
#define N 12
const int n = 12;
#undef N //取消宏定义后,即使在f1函数中,N也无效了
#define N 21//取消后可以重新定义
}
是否可以做函数参数
宏定义不能作为参数传递给函数
const常量可以在函数的参数列表中出现
四种强制类型转换
statac 全局变量、局部变量、函数与普通全局变量、局部变量、函数有什么区别?static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?
全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。 这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。 从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。 static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件 static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用; static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值; static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝 程序的局部变量存在于(堆栈)中,全局变量存在于(静态区 )中,动态申请数据存在于( 堆)中。
###static静态
在C语言中,关键字static有三个明显的作用:
1)在函数体内,一个被声明为静态的变量在这一函数被调用过程中维持上一次的值不变,即只初始化一次(该变量存放在静态变量区,而不是栈区)。
2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外访问。
3)在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
4)在类中的static成员变量意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见;
5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量(当然,可以通过传递一个对象来访问其成员)。