C++学习之第四天

阅读: 评论:0

C++学习之第四天

C++学习之第四天

一、简答题

1.当定义类时,编译器会为类自动生成哪些函数?这些函数各自都有什么特点?

定义类的时候,编译器自动给类生成默认的默认无参构造函数、拷贝构造函数、析构函数、赋值运算符函数。

解答:

1.构造函数:
    1.名字与类名相同,若程序员在类中自定义了有参构造函数和拷贝构造函数,默认构造函数也得自定义。
    2.实例化对象的时候自动调用,用于初始化成员数据,没有返回值,也没有返回类型。
    3.可以发生构造函数重载。

2.析构函数:
    1.类名前面加~,在对象销毁的时候自动调用,允许被显式调用,显式调用后,对应对象的数据会被清空。
    2.在每个类中有且只有一个,如果类中没有定义,编译器给类提供一个默认的空实现。

3.拷贝构造函数:
    1.编译器会给类提供一个默认的拷贝构造函数,进行简单的值拷贝;
    2.拷贝构造函数被调用的三个时机:1.用已存在的对象去初始化新的对象。
                               2.类的对象作为实参传递给函数,函数形形参接收实参时
                               3.函数的返回值是类的对象时。

4.赋值运算符函数:
    1.对象与对象之间使用赋值运算符,也是简单的浅拷贝,两对象指向同一片内存空间
    
    2.如果发生在堆空间,在进行释放的时候会出现重释放的问题,需要重写该函数进行深拷贝
    
    3.重写赋值运算符函数的步骤:1.自复制    2.释放左操作数     3.进行深拷贝     4.返回*this
    
    4.重写该函数时,返回类型中的&和形参中的&不能去掉的原因:Computer &operator=(const Computer &rhs)
    
         4.1.函数的返回类型是【类】类型,*this是类对象本身,在执行return语句的时候会满足拷贝构造函数调用             时机3,去执行拷贝构造函数,会增加开销。
        
        4.2.形参中的&如果去掉,在实参参数传递给形参的时候,也会去调用一次拷贝构造函数,增加开销
        
        4.3.返回的类型不能改,如果返回的是void类型,赋值函数只支持一次性赋值。

Computer类中重写赋值运算符

Computer &Computer::operator=(const Computer &rhs)
{cout << "Computer &operator=(const Computer &)" << endl;if(this !=  &rhs)//1、自复制{delete [] _brand;//2、释放左操作数_brand = nullptr;_brand = new char[strlen(rhs._brand) + 1]();//3、深拷贝strcpy(_brand, rhs._brand);_price = rhs._price;}return *this;//4、返回*this
}

2.什么是左值与右值,拷贝构造函数中的引用与const为什么不能去掉?

1.左值:可以进行取地址对象
右值:不可以取地址的对象,如临时对象/匿名对象,字面常量值都是右值

2.拷贝构造函数中的const和&为什么不能去掉

(1)拷贝构造函数的引用符号不能去掉。
    引用符号去掉之后,拷贝构造函数的实参与形参结合会发生拷贝,则会触发继续调用拷贝函数,从而形成递归,无限调用下去。栈空间大小有限,这样最后会导致stackoverflow。
    使用引用相当于给传进来的变量换个名字,因此不会再调用拷贝构造函数。
    
(2)拷贝构造函数的const关键字不能去掉。
    当传递给函数形参的参数是右值的时候,非const左值引用不能绑定右值。
    const关键字去掉之后,会导致实参与形参结合时,临时对象作为右值不能传递给左值,从而报错。

3.this指针是什么?

1.this指针的本质是一个指针常量。Person *const this 
2.this指针隐藏在非静态成员函数参数的第一个位置,指向对象本身。
3.*this,就是实例化对象本身。以下例子,相当于this =&pNull,那么*this解引用后,就是对象本身pNull.

this指针代码理解

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class NullPointCall 
{
public:NullPointCall(int test):_test(test){//this指针指向一段内存,内存里存储着pNull对象cout << this << endl;//this的地址,和对象pNull的地址一样cout << (*this)._test << endl;//输出10//*this=pNull,等同在外面调用了pNull._test}
private:int _test; 
}; 
int main()
{int a = 10;NullPointCall pNull(a);cout << &pNull << endl;//实例化对象pNull的地址和this一样system("pause");return EXIT_SUCCESS;
}

4.类中的数据成员必须在构造函数列表中初始化的3种情况?

1.常量数据成员-const修饰的变量不允许被修改、赋值,因此必须在列表中初始化

2.引用数据成员-int &ref,引用不能独立存在,在定义的时候必须初始化绑定到某个变量,因此必须在列表中初始化。

3.类对象数据成员-一个类作为另一个类的数据成员,如果不在列表中初始化,会调用其无参构造函数。

5.静态数据成员的初始化在哪里,需要注意什么?

1.用static修饰的数据成员:
(1)静态数据成员保存在全局/静态区,不占用对象的存储空间(如sizeof得到的结果不含静态数据成员大小)。

(2)在类内进行声明,在类外进行初始化,初始化时不包含static关键字

(3)被该类的所有对象所共享,可以通过实例化对象或者类名来访问。

2.用static修饰的成员函数。

(1)没有this指针

(2)只能访问静态数据成员

(3)被所有对象所共享

6.常函数和常对象

1.常函数.void showPerson()const
    1.修饰成员函数中的this指针,让指针指向的内容不允许被修改;
    
    2.本质:this指针的本质为:Person *const this,
           成员函数后加const等同于:const Person *const this;使this所指向的内容也不允许被修改
           
    3.但用mutable定义的数据成员,在常函数内允许被修改
    -即在常成员函数内,不允许修改普通成员,但可以修改用mutable定义的数据成员

2.常对象:
    1.常对象只允许访问常函数和mutable定义的成员数据,不允许访问普通成员。

优势:
    2.非常对象(const实例化的对象)允许访问常函数或者mutable修饰的数据成员。

二、写出下面程序结果。

1、写出以下程序运行的结果。

#include <math.h>
#include <iostream>using std::endl;
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构造函数被调用    //myp1(1,1)
point构造函数被调用    //myp2(4,5)
X = 4 Y= 5 Point拷贝构造函数被调用  //为什么有四个:1.对象作为实参传递调用拷贝构造函数
X = 1 Y= 1 Point拷贝构造函数被调用    //参数传递从右到左        
X = 1 Y= 1 Point拷贝构造函数被调用    //2.已存在的对象作为新实例化对象的初始值,
X = 4 Y= 5 Point拷贝构造函数被调用
Distance构造函数被调用 //myd(myp1, myp2)
The distance is:5

2、写出以下程序运行的结果。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include<string>
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;}
};
void test01()
{MyClass obj1(1), obj2(2);//1,2MyClass obj3 = obj1;//2,4,4,4
}
int main()
{test01();system("pause");return EXIT_SUCCESS;
}

运行结果

122444

分析

//区别赋值运算符函数的使用和拷贝构造函数的区别

Myclass obj3 = obj1; //实例化对象隐式定义,相当于Myclass obj3(obj1)
 
obj3 = obj1;  /* 这是对象的赋值运算*/

3、不考虑任何编译器优化(如:NRVO),下述代码的第10#会发生

#include <iostream>using std::cout;
using std::endl;class B
{
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;
}

程序运行结果

B()  //B b1 
B()  //B b2
B func(const B &)
B(const B&)  //func(b1) = rhs
B &operator=(const B &s) //b2 = func(b1)
~B() // b2
~B() // func(b1)
~B() //b1

Linux下:g++ hwk3 -fno-elide-constructors
才能看到以上结果

三、编程题。

1、实现一个自定义的String类,保证main函数对正确执行

main.cpp

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//#include <string>
#include "myString.h"void test01()
{MyString str="abc"; //1.实现有参构造函数,//cout << str << endl;cout << str << endl;//2.实现打印对象的功能MyString p2 = str;//3.把已有对象作为初始值,重写拷贝构造函数cout << p2 << endl; p2 = "hello world"; //4.重写赋值运算符,cout << p2 << endl;str= p2; //5.重写赋值运算符,进行深拷贝cout << str << endl;}int main() {test01();system("pause");return EXIT_SUCCESS;
}

myString.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include<string>
class MyString
{friend ostream &operator<<(ostream &cout, const MyString &p);//
public://1.重写左移运算符,只能写在外面,设置友元,实现直接把对象打印,能打印出其内容MyString(){};MyString(const char *str1);//2.默认有参构造函数,实现MyString str="abc"或者 str("abc")MyString(const MyString &p_str);//拷贝构造函数,实现MyString p1=p2MyString &operator=(const char *str1)//重载赋值运算符,实现p1 = "abcdse"{if (this->str != NULL) {delete[]str;str = NULL;}str = new char[strlen(str1) + 1];strcpy(str, str1);return *this;}MyString &operator=(const MyString &p_str) //实现深拷贝 p1 = p2{if (this!= &p_str) //1.自复制{delete[]str; //2.释放左操作数str = NULL;str = new char[strlen(p_str.str) + 1];strcpy(str, p_str.str); //深拷贝}return *this;//返回自身}~MyString();
private:char *str; //从堆空间申请一个数组来维护字符串
};//默认有参构造 s1("abc")
MyString::MyString(const char *str1)
{str = new char[strlen(str1) + 1];strcpy(str, str1);//cout << str << endl;
}//2.拷贝构造 s1(s2)
MyString::MyString(const MyString &p_str)
{if (this != NULL){delete[] str;str = nullptr;str = new char[strlen(p_str.str) + 1];strcpy(str, p_str.str);}
}//3.cout<<s1直接打印对象能打印出内容
ostream &operator<<(ostream &cout, const MyString &p)
{cout << p.str;return cout;
}//析构,释放堆空间
MyString::~MyString()
{if (str){delete[]str;str = nullptr;}
} 

2、用C++实现一个单链表

1.一个对象代表一个单链表,实例化一个对象就开启初始化

2.按指定位置插入单链表,实现插入任意类型的数据

3.返回单链表的长度。

4.遍历单链表,提供回调函数打印任意类型数据

5.按指定位置删除链表元素

6.按指定值删除单链表元素,提供对比回调函数

7.清空单链表

main.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include<string>
#include "linkList.h"struct Person//测试数据
{char name[64];int age;
};void myPrint(void *data) //提供打印回调函数,根据不同的类型提供不同的打印函数
{struct Person *p = (struct Person *)data;cout << "姓名:" << p->name<< ",年龄:" << p->age<<endl;
}
bool myStructCompare(void *data1, void *data2)//提供对比回调函数
{struct Person *p1 = (struct Person *)data1;struct Person *p2= (struct Person *)data2;return strcmp(p1->name, p2->name) == 0 && p1->age == p2->age;
}
void test01()//测试1
{//创建数据struct Person p1 = {"小乔", 18 };struct Person p2 = {"周瑜", 20 };struct Person p3 = {"大乔", 30 };struct Person p4 = {"孙策", 32 };struct Person p5 = {"诸葛亮", 36 };LList list1;list1.insert_LinkList(0, &p1);list1.insert_LinkList(1, &p2);list1.insert_LinkList(1, &p3);list1.insert_LinkList(1, &p4);list1.insert_LinkList(1, &p5);list1.foreach_List(myPrint);//遍历单链表cout <<"当前链表长度为:"<< list1.list_Length() << ve_By_pos(1);//删除链表中第一个元素list1.foreach_List(myPrint);cout << "当前链表长度为:" << list1.list_Length() << ve_By_Value(&p5, myStructCompare);//删除诸葛亮list1.foreach_List(myPrint);cout << "当前链表长度为:" << list1.list_Length() << endl;list1.clear_List();cout << "当前链表长度为:" << list1.list_Length() << endl;}
void myInPrint(void *data)//回调函数打印整型数据
{int *num = (int *)data;cout << *num << endl;
}
bool myIntCompare(void *data1, void *data2)//回调函数整数对比
{int *num1 = (int *)data1;int *num2 = (int *)data2;return *num1 == *num2;
}
void test02()
{//创建数据int p1 = 18;int p2 = 20;int p3 = 30;int p4 = 32;int p5 = 36;LList list1;list1.insert_LinkList(0, &p1);list1.insert_LinkList(1, &p2);list1.insert_LinkList(1, &p3);list1.insert_LinkList(1, &p4);list1.insert_LinkList(1, &p5);list1.foreach_List(myInPrint);//遍历单链表cout << "当前链表长度为:" << list1.list_Length() << endl;cout << "删除链表中第一个元素" << ve_By_pos(1);//删除链表中第一个元素list1.foreach_List(myInPrint);cout << "当前链表长度为:" << list1.list_Length() << ve_By_Value(&p5, myIntCompare);//删除36cout << "删除36" << endl;list1.foreach_List(myInPrint);cout << "当前链表长度为:" << list1.list_Length() << endl;cout << "清空链表" << endl;list1.clear_List();cout << "当前链表长度为:" << list1.list_Length() << endl;}
int main()
{//test01();test02();system("pause");return EXIT_SUCCESS;
}

linkList.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include<string>
struct LinkNode
{void *data;struct LinkNode *next;
};class LList
{
public:LList();//1.初始化构造函数//2.单链表的插入void insert_LinkList(int pos, void * data);//3.返回链表长度int list_Length(){return length;}//4.打印链表中的每个元素void foreach_List(void(*myForeach)(void *));//5.按位序删除单链表中的每个元素void remove_By_pos(int pos);//6.按指定值来删除单链表中的元素void remove_By_Value(void *data, bool(*myCompare)(void *, void *));//7.清空单链表void clear_List();~LList() {}; //析构函数private:struct LinkNode pHeader;int length;
};

linkList.cpp

#pragma once
#include "linkList.h"LList::LList()//1.初始化构造函数
{ = NULL; //头指针置空length = 0; //初始化链表长度
}//2.单链表的插入:指定位置插入,往第pos位置插入数据
void LList::insert_LinkList(int pos, void * data)
{if (data == NULL){return; //数据为空}if (pos<0 || pos> length){pos = length+1;//pos不合法就进行尾插}//找到第pos位置的前驱struct LinkNode *pCurrent = &pHeader;for (int i = 0; i < pos; i++){pCurrent = pCurrent->next;}//遍历结束后,pCurrent指向第pos位置的前驱struct LinkNode *newNode =(struct LinkNode *) malloc(sizeof(struct LinkNode));//申请新空间newNode->data = data;newNode->next = pCurrent->next;//进行尾插pCurrent->next = newNode;length++;
}//3.打印链表中的每个元素
void LList::foreach_List(void(*myForeach)(void *))//提供回调函数打印任意类型,桥梁
{if (length == 0)//链空{return;}struct LinkNode *pCurrent = ;//用于遍历当前结点for (int i = 1; i <= length; i++){myForeach(pCurrent->data);//回调函数pCurrent = pCurrent->next;}
}//4.按指定位置来删除单链表中的每个元素
void LList::remove_By_pos(int pos)
{if (length == 0){cout << "当前链表为空,没得删" << endl;return;}if (pos<1 || pos>length){cout << "pos值不正确,删除失败" << endl;return;}//找到pos的前驱结点struct LinkNode *pCurrent = &pHeader;for (int i = 1; i < pos; i++){pCurrent = pCurrent->next;}struct LinkNode *pDel = pCurrent->next;pCurrent->next = pDel->next;free(pDel);length--;}//5.按指定值来删除单链表的元素
void LList::remove_By_Value(void *data, bool(*myCompare)(void *, void *))
{if (data == nullptr){return;}if (myCompare == nullptr)//用户不提供回调函数,直接返回{return;}if (length == 0){cout << "链表为空" << endl;return;}struct LinkNode *pCurrent = ;//指向第一个元素for (int i = 1; i <= length; i++){if (myCompare(pCurrent->data, data))//对比规则根据数据类型的不同来编写{remove_By_pos(i);//按指定位置删除break;}pCurrent = pCurrent->next;}
}//6.清空单链表
void LList::clear_List()
{if (length == 0){return;}while ( != nullptr){struct LinkNode *pCurrent = ; = pCurrent->next;	free(pCurrent);}length = 0;
}

3、实现上课时候的单例模式的代码

1.通过一个类,只能实例化唯一的一个对象

2.将构造函数私有化后,类就不能创建多个对象,否则会出错

        -可以通过类的静态变量来初始化唯一一个对象,类内声明,类外初始化,

        -将这个对象变量私有化,通过静态成员函数来提供只读接口

        -将拷贝构造函数私有化,防止间接实例化对象

3.对外提供一个getinstance 接口,将指针返回,
4.应用场景:全局唯一的资源,日志记录器、网页库、字典库

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include<string>//设计需求:一个类只能创建一个对象
//
//应用场景:全局唯一的资源,日志记录器、网页库、字典库class Singleton
{
public:static Singleton *getInstance()//只能用静态成员函数来访问静成员{if (_pInstance == nullptr) //外界通过该函数获取该唯一对象时,只有在第一次获取的时候申请堆空间{_pInstance = new Singleton(); //}return _pInstance;//已有对象的情况下,多次调用此函数,只会返回唯一的指针}static void destroy(){if (_pInstance)//如果堆空间存在,才释放{delete _pInstance;_pInstance = nullptr;}}private:Singleton(){cout << "Singleton()" << endl;}~Singleton(){cout << "~Singleton()" << endl;}//类内只声明static Singleton * _pInstance; //为了保证申请出来的堆空间唯一,需要一个成员来保存堆的地址
};
Singleton *Singleton::_pInstance = nullptr;//类外定义和初始化int main()
{Singleton *ps1 = Singleton::getInstance();Singleton *ps2 = Singleton::getInstance();//再次获取,会获得同样的地址cout << "ps1 = " << ps1 << endl;cout << "ps2 = " << ps2 << endl;Singleton::destroy();//手动释放Singleton::destroy();Singleton::destroy();Singleton::destroy();Singleton::destroy();/* delete ps1;//error *//* delete ps2; */return 0;system("pause");return EXIT_SUCCESS;
}

4、上课的时候,单例模式的代码中,对象是放在堆上的,大家可以看看除了堆还有哪些地方可以存放这个唯一的对象,可以写出这样的代码?

提示:可以看看全局的、静态的、栈上的。

待解答~

四、算法题(选做,如果做不出来可以不做)

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。

待完成

本文发布于:2024-01-29 13:28:20,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170650610515604.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:第四天
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23