[C++语法规则很多,要落实下来,得通过多敲代码来理解,看N遍不如写一次;在写代码的过程中,会碰到其它你不曾碰到过的编译问题,切记程序是调试出来的;再就是通过练习,把敲代码的速度提升上来,熟悉键盘,培养写代码的感觉]
一、简答题
1、当定义类时,编译器会为类自动生成哪些函数?这些函数各自都有什么特点?
名称 | 特点 |
---|---|
构造函数 | 1.默认情况下,编译器会自动生成构造函数 。形式上是函数名与类名相同,没有返回值,但是可以传参数,故构造函数可以重载 。 2 .全局对象和全局静态对象的构造函数在main()函数执行之前就被调用,局部静态对象的构造函数是程序第一次执行到才被调用。 |
析构函数 | 1.函数名为~类名、没有返回值、没有返回类型,当程序执行后,系统将自动调用自动创建的析构函数,将对象释放。 2.默认的析构函数不能删除new在自由存储器中分配的对象和对象成员,需要自己自定义析构函数,然后显示使用delete运算符来释放new运算符分配的内存 |
拷贝函数 | 调用时机:1. 当用一个已经存在的对象初始化另一个新对象时,会调用拷贝构造函数。 2. 当实参和形参都是对象,进行实参与形参的结合时,会调用拷贝构造函数。 3. 当函数的返回值是对象,函数调用完成返回时,会调用拷贝构造函数。注意:(const Computer & rhs) 中 const一般不可去掉,而&不可去掉。 |
赋值运算符函数 | 1.函数名为operator=、返回值为类名、参数为const类&,用来对已存在的对象进行赋值。 2.定义类时引用符号不可以去掉,若去掉会多执行一次拷贝构造函数,效率会降低。不可去掉const,若右操作数是右值(不可取地址),会产生废const左值引用不大于右值的报错 3.返回类型不能是void型,否则函数连载会发生错误 |
2、什么是左值与右值,拷贝构造函数中的引用与const为什么不能去掉?
左值:可以取地址,内存中有实际存储 右值:不能取地址,例如临时对象、匿名对象、临时变量、字面值常量(如10)都属于右值
3、this指针是什么?
1.指针常量; 2.用来指向对象本身,隐藏于非静态成员函数的第一个参数位置
二、写出下面程序结果。
1、写出以下程序运行的结果。
#include <math.h> #include <iostream> using std::cout; using std::endl; class Point { public: Point(int xx = 0, int yy = 0) { X = xx; Y = yy; cout << "point构造函数被调用" << endl; } Point(Point &p); int GetX() { return X; } int GetY() { return Y; } private: int X,Y; }; Point::Point(Point &p) { X = p.X; Y = p.Y; cout << "X = " << X << " Y=" << Y << "Point拷贝构造函数被调用" << endl; } class Distance { public: Distance(Point xp1, Point xp2); double GetDis() { return dist; } private: Point p1,p2; double dist; }; Distance::Distance(Point xp1, Point xp2) : p1(xp1) ,p2(xp2) { cout << "Distance构造函数被调用" << endl; double x = double(p1.GetX() - p2.GetX()); double y = double(p1.GetY() - p2.GetY()); dist = sqrt(x * x + y * y); } int main() { Point myp1(1,1), myp2(4,5); Distance myd(myp1, myp2); cout << "The distance is:" ; cout << myd.GetDis() << endl; return 0; }
point构造函数被调用 point构造函数被调用 X = 4 Y = 5 Point拷贝构造函数被调用 X = 1 Y = 1 Point拷贝构造函数被调用 X = 1 Y = 1 X = 4 Y = 5 Point拷贝构造函数被调用 Distance构造函数被调用 The distance is:5 (本题题点: ) Distance::Distance(Point xp1, Point xp2) : p1(xp1) ,p2(xp2) 形参实参结合要调用一次拷贝构造函数,初始化赋值也要调用一次拷贝构造函数
2、写出以下程序运行的结果。
#include<iostream> using namespace std; class MyClass { public: MyClass(int i = 0) { cout << i; } MyClass(const MyClass &x) { cout << 2; } MyClass & operator=(const MyClass &x) { cout << 3; return *this; } ~MyClass() { cout << 4; } }; int main() { MyClass obj1(1), obj2(2); MyClass obj3 = obj1; obj2 = obj1 return 0; }
122444
3、不考虑任何编译器优化(如:NRVO),下述代码的第10#会发生
#include <iostream> using std::cout; using std::endl; classB { public: B() //构造函数 { cout << "B()" << endl; } ~B() //析构函数 { cout << "~B()" << endl; } B(const B &rhs) //拷贝构造函数 { cout << "B(const B&)" << endl; } B &operator=(const B &rhs) //运算符重载函数 { cout << "B &operator=(const B &s)" << endl; return *this; } }; B func(const B &rhs) { cout << "B func(const B &)" << endl; return rhs; } int main(int argc, char **argv) { B b1,b2; b2=func(b1);//10# return 0; }
b1 是实参,将值传给func()函数的形参rhs, func()函数输出 B func(const B &) ,再将rhs的值作为func()的值,这个过程调用了拷贝构造函数,将rhs传给func(),最后赋值给b2,复制的过程调用的运算符重载函数,输出B &operator=(const B &s),最后b2本身赋值b2
三、编程题。
1、实现一个自定义的String类,保证main函数对正确执行
#include <iostream> #include <string.h> #include <string> using std::cout; using std::endl; class String { public: String(); String(const char *pstr); String(const String &rhs); String &operator=(const String &rhs); ~String(); void print(); private: char * _pstr; }; int main() { String str1; str1.print(); String str2 = "Hello,world"; String str3("wangdao"); str2.print(); str3.print(); String str4 = str3; str4.print(); cout<<"111111111111111111111111"<<endl; str4 = str2; str4.print(); return 0; } /*下面为实现过程*/ //构造函数 String::String() { _pstr = new char[1](); } //重载构造函数 String::String(const char *pstr) { if(pstr==NULL){ _pstr=new char[1](); }else{ _pstr=new char[strlen(pstr)+1]; strcpy(_pstr,pstr); } } //拷贝构造函数 String::String(const String &rhs) { _pstr = new char[strlen(rhs._pstr) + 1] (); strcpy(_pstr , rhs._pstr); } //赋值函数 String & String::operator=(const String &rhs) { if(this!=&rhs){ delete [] rhs._pstr; _pstr=NULL; _pstr = new char[strlen(rhs._pstr) + 1] (); strcpy(_pstr , rhs._pstr); } return *this; } //析构函数 String::~String(){ delete[] _pstr; cout << "析构函数!" << endl; } //打印成员函数 void String::print(){ if(NULL==_pstr){ //没有信息就不需要打印 return; } puts(this->_pstr); }
老师标准答案:
#include <string.h>
#include <iostream>
using std::cout;
using std::endl;
class String
{
public:
String()
: _pstr(nullptr)
/* : _pstr(new char[1]()) */
{
cout << "String()" << endl;
}
String(const char *pstr)
: _pstr(new char[strlen(pstr) + 1]())
{
cout << "String(const char *)" << endl;
strcpy(_pstr, pstr);
}
String(const String &rhs)
: _pstr(new char[strlen(rhs._pstr) + 1]())
{
cout << "String(const String &)" << endl;
strcpy(_pstr, rhs._pstr);
}
String &operator=(const String &rhs)
{
cout << "String &operator=(const String &)" << endl;
if(this != &rhs)
{
delete [] _pstr;
_pstr = nullptr;
_pstr = new char[strlen(rhs._pstr) + 1]();
strcpy(_pstr, rhs._pstr);
}
return *this;
}
~String()
{
cout << "~String()" << endl;
if(_pstr)
{
delete [] _pstr;
_pstr = nullptr;
}
}
void print() const
{
if(_pstr)
{
cout << _pstr << endl;
}
else
{
cout << "nullptr == _pstr " << endl;
}
}
private:
char *_pstr;
};
int main(void)
{
String str1;
str1.print();
cout << endl;
//C++ C
String str2 = "Hello,world";//String("Hello,world")
String str3("wangdao");
/* String b();//函数声明 */
/* String s1; */
str2.print();
str3.print();
cout << endl;
String str4 = str3;
str4.print();
cout << endl;
str4 = str2;
str4.print();
return 0;
}
2、用C++实现一个双向链表
#include <iostream> #include <string.h> #include <string> using namespace std; struct Node { int data; Node * pre; Node * next; }; class List { public: List(); ~List(); void push_front(int data);//在头部进行插入 void push_back(int data);//在尾部进行插入 void pop_front();//在链表头部进行删除 void pop_back();//在链表的尾部进行删除 bool find(int data);//在链表中进行查找 void insert(int pos, int data);//在指定位置后面插入pos void display() const; //打印链表 void erase(int data);//删除一个指定的节点 private: Node * _head; Node * _tail; int _size; }; //构造函数 List::List() :_head(NULL),_tail(NULL),_size(0) { } //头插法插入节点 void List::push_front(int data) { Node *p = new Node[1](); p->data = data; p->next = NULL; p->pre = NULL; if(NULL == _head) { _head = p; _tail = p; }else { p->next = _head; _head->pre = p; _head = p; } _size++; } //尾部插入 void List::push_back(int data) { Node *p = new Node[1]; p->data = data; p->next = NULL; p->pre = NULL; if(NULL == _tail) { _tail = p; _head = p; }else { p->pre = _tail; _tail->next = p; _tail = p; } _size--; } //在链表头部进行删除 void List::pop_front() { if(_size != 0) { //链表不空时,进行删除 Node *s = _head; //定义一个指针指向第一个元素 s->next->pre = NULL; // _head = s->next; s->next = NULL; delete s;// s = NULL; _size--; } } void List::pop_back() { if(_size != 0) { //链表不空时,进行删除 Node *s = _tail; s->pre->next = NULL; _tail = s->pre; s->pre = NULL; delete s; _size--; } } bool List::find(int data) { if(_size != 0) { Node *s = _head; while(s) { if(s->data == data) { cout << "找到!" << endl; return true; } s = s->next; } cout << "没找到!" << endl; } } void List::insert(int pos, int data) { int n = 1; Node *s = _head; while (n != pos ) { s = s->next; n++; } //分头中尾插入 Node *p = new Node[1](); p->data = data; }
四、算法题
1、矩阵中的路径
题目:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用下划线标出)。但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
2、剪绳子
题目:给你一根长度为n绳子,请把绳子剪成m段(m、n都是整数,n>1并且m≥1)。每段的绳子的长度记为k[0]、k[1]、……、k[m]。k[0]*k[1]*…*k[m]可能的最大乘积是多少?例如当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到最大的乘积18。
3、二进制中1的个数
题目:请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如:把9表示成二进制是1001,有2位是1。因此如果输入9,该函数输出2。
4、数组中出现次数超过一半的数字
题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1, 2, 3, 2, 2, 2, 5, 4, 2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。