C++学习之第十天-继承

一、选择题

下面叙述错误的是(A)

A.基类的protected成员在派生类中仍然是protected

B.基类的protected成员在public派生类中仍然是protected的

C.基类的protected成员在private派生类中是private的

D.基类的protected成员不能被派生类的对象访问

解析:
权限运算规则:
public > protecetd > private
(继承方式 与 基类中权限 )取交集 = 派生类中权限

2、下列对派生类的描述中,(D)是错误的。

A.一个派生类可以作为另一个派生类的基类

B.派生类至少有一个基类

C.派生类的成员除了它自己的成员外,还包含了它的基类成员

D.派生类中继承的基类成员的访问权限到派生类保持不变

3、派生类的对象对它的哪一类基类成员是可以访问的?(A)

        A.公有继承的基类的公有成员 B. 公有继承的基类的保护成员

        C. 公有继承的基类的私有成员 D. 保护继承的基类的公有成员

4、关于多继承二义性的描述,(D)是错误的。

A.派生类的多个基类中存在同名成员时,派生类对这个成员访问可能出现二义性

B.一个派生类是从具有共同的间接基类的两个基类派生来的,派生类对该公共基类的访问可能出现二义性

C.解决二义性最常用的方法是作用域运算符对成员进行限定

D.派生类和它的基类中出现同名函数时,将可能出现二义性

派生类和它的基类中出现同名函数,如果与基类中虚函数同名,则是覆盖。
如果和非虚函数同名,则是隐藏。

5、设有基类定义:

class Base
{   
private: 
	int a;
protected: 
	int b;
public: 
	int c;
};

派生类采用何种继承方式可以使成员变量b成为自己的私有成员( A )

A. 私有继承 B.保护继承C. 公有继承 D.私有、保护、公有均可

二、填空题

1、在继承机制下,当对象消亡时,编译系统先执行[ 派生类 ] 的析构函数,然后才执行 [ 派生类中成员对象 ] 的析构函数,最后执行 [基类] 的析构函数。

三、改错题以及写结果题。

1、指出并改正下面程序中的错误。

#include<iostream>

using std::cout;
using std::endl;

class Point
{   
public:
    Point(int a=0, int b=0) 
	{
		x = a; 
		y = b;
	}
    void move(int xoffset,int yoffset) 
	{
		x += xoffset; 
		y += yoffset;
	}
	

    int getx() 
    {	
    	return x;	
    }
    
    int gety() 
    {	
    	return y;	
    }

private:
	int x,y;
};

class Rectangle
:protected Point
{    
public:
	Rectangle(int x, int y, int l, int w)
	: Point(x,y)
	{   
		length = l;
		width  = w;
	}
	

	int getLength()
	{	
		return length;	
	}
	
	int getWidth()	
	{	
		return width;	
	}

private:
	int length;
	int width;
};
int main()
{ 
	Rectangle r(0, 0, 8,4);
 	r.move(23,56);
	cout << r.getx() 
	     << "," << r.gety() 
		 << "," << r.getlength() 
		 << "," << r.getwidth() << endl;
		 

	return 0;

}

答:三处错误:

​ 1.protected---->public

​ 2.r.getlength() -->r.getLength();

​ 3.r.getwidth() --->r.getWidth();

2、指出并改正下面程序中的错误。

#include<iostream>

using std::cout;
using std::endl;

class A
{
 public:
    int x;
    A(int a = 0) 
	{
		x = a;
	}
    void display() 
	{ 
		cout<< "A.x = " << x << endl;
	}
};
class B
{ 
public:
	int x;
    B(int a = 0) 
	{
		x=a;
	}
 	

	void display() 
	{
		cout<<"B.x = " << x << endl; 
	}

};

class C
:public A
,public B
{   

 public:
    C(int a, int b, int c) 
	: A(a)
	, B(b)
    {    
		y=c;  
	}
	

   	int gety() 
   	{ 
   		return y;
   	}

private:
	int y;
};
int main()
{ 
	C myc(1,2,3);
 	myc.x = 10;
 	myc.display();

	return 0;

}

答:

访问基类中的成员变量和函数需要加作用域,避免二义性:
myc.x = 10;----->myc.B::x=10或者myc.A::x=10
myc.display();------->myc.B::display()或者myc.A::display();

3、看程序写结果

#include<iostream>

using std::cout;
using std::endl;

class Base
{     
public:
    Base(int n)
    {
      cout <<"Constucting base class" << endl;
      _ix = n;
    }
	

    ~Base()
    {
    	cout <<"Destructing base class" << endl;
    }
    
    void showX()
    {
    	cout << _ix << ",";
    }
    
    int getX()
    {
    	return _ix;
    }

private:
	int _ix;
};

class Derived
:public Base
{     
public:
	Derived(int n, int m, int p)
	: Base(m)
	, _base(p)
	{
		cout << "Constructing derived class" <<endl;
        j = n;
    }

    ~Derived()
    {
    	cout <<"Destructing derived class"<<endl;
    }
    
    void show()
    {
    	Base::showX();
        cout << j << "," << _base.getX() << endl;
    }

private:
	int j;
    Base _base;
};
int main()
{ 
	Derived obj(8,13,24);
 	obj.show();

	return 0;

}

结果:

Constucting base class
Constucting base class
Constructing derived class
13,8,24
Destructing derived class
Destructing base class
Destructing base class

四、简答题

1、三种继承方式对于基类成员的访问权限是怎样的?

1. 派生类中的访问权限计算公式:
( 继承形式 与 基类成员权限 ) 取交集 = 派生类中访问权限

2.基类成员中的私有成员继承后都不可直接访问

3.派生类中对象访问权限: 派生类以共有方式继承基类,实例化出来的的对象只可访问基类的公有成员

2、继承中有哪些内容是不能进行继承的?

1.构造函数
2.析构函数
3.用户重载的operator new/delete运算符
4.用户重载的=运算符
5.友元关系

3、多基派生会产生的问题有哪些?怎样解决?

(1)成员名冲突二义性
问题原因:所继承的多个基类中存在同名成员,从而吸收到了不同基类的同名成员
解决方法:通过基类的命名域访问从相应基类中继承而来的成员

菱形继承二义性
问题原因:所继承的基类中有多个基类又有着共同基类,从而在该派生类中会吸收来自不同路径的共同基类成员的多重拷贝
解决方法:(1)通过基类的命名域访问从相应基类中继承而来的成员。(2)使用虚继承

五、编程题。

1、编写一个圆类Circle,该类拥有:

① 1个成员变量,存放圆的半径; ② 两个构造方法 Circle( ) // 将半径设为0 Circle(double r ) //创建Circle对象时将半径初始化为r ③ 三个成员方法 double getArea( ) //获取圆的面积 double getPerimeter( ) //获取圆的周长 void show( ) //将圆的半径、周长、面积输出到屏幕

2、编写一个圆柱体类Cylinder,它继承于上面的Circle类,还拥有: ① 1个成员变量,圆柱体的高;② 构造方法 Cylinder (double r, double h) //创建Circle对象时将半径初始化为r ③ 成员方法double getVolume( ) //获取圆柱体的体积 void showVolume( ) //将圆柱体的体积输出到屏幕 编写应用程序,创建类的对象,分别设置圆的半径、圆柱体的高,计算并分别显示圆半径、圆面积、圆周长,圆柱体的体积。

#include <iostream>
using namespace std;

class Circle
{
public:
    Circle()
    :_r(0)
    {
        cout<<"Circle的无参 构造函数"<<endl;
    }

    Circle(double r)
    :_r(r)
    {
        cout<<"Circle的有参构造函数"<<endl;
    }

    double getArea()//获取圆的面积
    {
        return 3.14*_r*_r;
    }
    
    double getPerimeter()//获取圆的周长
    {
        return 2.0*3.14*_r;
    }

    void show()
    {
        cout<<"半径:"<<_r<<endl
            <<"面积:"<<getArea()<<endl
            <<"周长:"<<getPerimeter()<<endl;
    }
private:
    double _r;
};
class Cylinder:public Circle
{
public:
    Cylinder(double r, double h)
    :Circle(r) //调用基类构造函数来初始化半径
    ,_h(h)
    {
        cout<<"Cylinder有参构造函数"<<endl;
    }
    
    double getVolume()//获取圆柱体体积
    {
        return getArea()*_h;
    }

    void showVolumn()
    {
        cout<<"Cylinder的体积:"<<getVolume()<<endl;
    }

private:
    double _h;
};

void test01()
{
    Circle s1(10);
    s1.show();

    Cylinder c(10,10);
    c.showVolumn();
}
int main()
{
    test01();
    return 0;
}

运行结果

Circle的有参构造函数
半径:10
面积:314
周长:62.8
Circle的有参构造函数
Cylinder有参构造函数
Cylinder的体积:3140

3、构建一个类Person,包含字符串成员name(姓名),整型数据成员age(年龄),成员函数 display()用来输出name和age。构造函数包含两个参数,用来对name和age初始化。构建一个类Employee由Person派生,包含department(部门),实型数据成员salary(工资),成员函数display()用来输出职工姓名、年龄、部门、工资,其他成员根据需要自己设定。主函数中定义3个Employee类对象,内容自己设定,将其姓名、年龄、部门、工资输出,并计算他们的平均工资。

#include <iostream>
#include <string.h>
using namespace std;
class Person
{
public:
    Person(string name, int age)
    :_name(name)
     ,_age(age)
    {
        cout<<"Person()"<<endl;
    }

    void display()
    {
        cout<<"name:"<<_name<<endl
            <<"age:"<<_age<<endl;
    }
private:
    string _name;
    int _age;
};

class Employee:public Person
{
public:
    Employee(string name,int age, string department, float salary)
    :Person(name,age),//名字和年龄要调用基类的构造函数来初始化
    _department(department),
    _salary(salary)
    {
        CounSalary+=salary;
        Count_employee +=1;
        cout<<"Employee()"<<endl;
    }
    void display()
    {
        cout<<Count_employee<<"号员工信息如下:"<<endl;
        this->Person::display();//名字和年龄的显式需要用作用域形式来调用
        cout <<"department:"<<_department<<endl
            <<"Salary:"<<_salary<<endl;
        cout<<endl;
    }
    static void show_Ave_Salary()
    {
        cout<<"总员工:"<<Count_employee<<endl
            <<"平均工资:"<<CounSalary/Count_employee<<endl;
    }
private:
    string _department;//部门
    float _salary;//工资
    static float CounSalary;//员工总薪资
    static int Count_employee;//总员工人数
};
float Employee::CounSalary = 0;
int Employee::Count_employee = 0;

void test01()
{
    Employee e1("Tom",12,"Wipasx",3000);
    e1.display();
    Employee e2("Gogo",14,"Testing",8999);
    e2.display();
    Employee e3("Peter",21,"FA", 7878);
    e3.display();
    cout<<"员工创建完毕"<<endl;
    Employee::show_Ave_Salary();
}
int main()
{
    test01();
    return 0;
}

运行结果

Person()
Employee()
1号员工信息如下:
name:Tom
age:12
department:Wipasx
Salary:3000

Person()
Employee()
2号员工信息如下:
name:Gogo
age:14
department:Testing
Salary:8999

Person()
Employee()
3号员工信息如下:
name:Peter
age:21
department:FA
Salary:7878

员工创建完毕
总员工:3
平均工资:6625.67

4、词频统计的作业再用map容器去实现一次,体验一下使用vector/map时程序执行的速度++dict[word];

Bible.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include<string>
#include <vector>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <iomanip>
#include <map>
#include <time.h>
class Dictionary
{
public:
	void read(const string &filename);//1.读取圣经,进行字符统计,把结果并且存入_dict vector
	
	//void dict_Sort();//排序

	void foreach_map();//遍历map

	void store(const string &filename);//2.从map中把结果写入新文件

private:
	map <string, int>_dict;
	string int2String(int number);//int类型转为字符串流,ostringstream

	void trim(string &s);//string字符串去掉首尾空格

	bool judgefullword(string &word);//判断单词是否正确


};

bible.cc

#include "Bible.h"


void Dictionary::read(const string &filename)//读取数据并且进行处理
{
	ifstream ifs(filename);//1.ifstream 从文件中读取数据
	if (!ifs.good())//文件不存在就退出
	{
		cerr << "ifstream is not good" << endl;
		return;
	}

	string word;
	while (ifs >> word)//2.默认以空格为分隔符,每次读一个单词
	{
		//istringstream ss(word); //把word变成string流
		//string key; //用来接收string
		//ss >> key; //key接收读出来的字符串

		//把key中的字符,大写变成小写
		for (int i = 0; i < word.size(); i++)//3.把单词的大写转成小写,非字母类型转为空格
		{
			if (word[i] >= 'A' && word[i] <= 'Z')
			{
				word[i] += 32;
			}

			if (word[i] >= 'a' &&word[i] <= 'z')
			{
				continue;
			}

			word[i] = ' '; //标点符号变成空格
		}

		trim(word); //4.去掉word的首尾空格,如果word全是空字符, 会把word置空,word.empty()会为真

		bool flag_no_error = judgefullword(word);//5.去掉首尾空格后,单词中间还有其他不合法字符,就跳过这个单词

		if (!word.empty()&&flag_no_error==true)//6.单词不为空,并且为合法单词
		{
			if (_dict.empty())//7.如果_dict为空,直接把单词插入map容器
			{
                _dict.insert(make_pair(word,1));//map容器插入
			}
			else
			{
				map<string, int>::iterator ret = _dict.find(word);
                //8.如果_dict不空,先判断_dict中是否有该单词,有的话,对应词频加1
				if (ret != _dict.end())	//ret是个map迭代器			
				{
					ret->second++;//ret是word在_dict中的位置,词频加1

				}
				else
				{
                    _dict.insert(make_pair(word,1));
				}
			}	
		}
		

	}
}

//string字符串去掉首尾空格
void Dictionary::trim(string &s)
{

	if (!s.empty())
	{
		s.erase(0, s.find_first_not_of(" "));
		s.erase(s.find_last_not_of(" ") + 1);
	}

}

//判断读出来的单词是否正确
bool Dictionary::judgefullword(string &word)
{
	for (int i = 0; i < word.size(); i++)
	{
		if (word[i]<'a' || word[i]>'z')
			return false;
	}
	return true;
}

void Dictionary::foreach_map()//遍历map
{
    int i = 1;
    for(map<string, int>::iterator it=_dict.begin(); it!=_dict.end();it++)
    {
        //cout<<it->first<<"\t"<<it->second<<endl;
		cout<< int2String(i) << "  " //单词序号,把int转成string
			<<setiosflags(ios::left)<<setw(32)<< it->first<<"\t"//单词对齐
			<< it->second<< endl;//词频
        i++;
    }
}
//把int转成string
string Dictionary::int2String(int number)
{
	ostringstream oss;
	oss << number;  //把number写入oss流
	return oss.str();//返回oss字符串
}



//把统计数据输出到文件
void Dictionary::store(const string &filename)
{
	ofstream ofs(filename);//ofstream,把结果数据输出到文件

	if (!ofs.good())
	{
		cerr << "ofstream is not good " << endl;
		return;
	}
	
#if 0
	for (int i = 0; i < _dict.size(); i++)
	{
		ofs <<int2String(i+1) <<"  "<<_dict[i]._word << "        " << _dict[i]._frequency << endl;
	}
#endif
	ostringstream my_ss;
    int i = 1;
	for (map<string,int>::iterator it=_dict.begin();it!=_dict.end();it++)
	{
		//1.把数据先对齐写入字符串流
		my_ss << int2String(i) << "  " //单词序号,把int转成string
			<<setiosflags(ios::left)<<setw(32)<<it->first<<"\t"//单词对齐
			<< it->second  << endl;//词频
        i++;
	}
	//cout << my_ss.str() << endl;
	//2.再把数据写入到文件
	ofs << my_ss.str() << endl;
	ofs.close();
}

main.cc

#include "Bible.h"

void test01()
{
	Dictionary s1;
    clock_t start, end;
    start = clock();//开始时间
	s1.read("The_Holy_Bible.txt"); //1.读取圣经数据,统计单词词频,存入map
	//s1.dict_Sort();		//2.对map中的单词进行排序
	s1.foreach_map();//3.遍历词典map,把结果输出到控制台
	s1.store("my_dict.txt");//4.把结果存入文件

    end = clock();//结束
    cout<<"Time:"<<double(end-start)/CLOCKS_PER_SEC <<"s"<<endl;
}
int main()
{

	test01();

	return EXIT_SUCCESS;
}

运行结果,使用map容器来完成,比vector容器速度快很多:

Time:0.851361s

(Day6仅使用vecotr)运行结果:

Time:12.584s

猜你喜欢

转载自blog.csdn.net/weixin_49278191/article/details/121198866