C++类和对象2

      public成员:外界可以访问
      private成员:外界不可访问,子类中不可访问
      protected成员:外界不可访问,子类(派生类)中可以访问,这样在派生类中可以访问某些外界不可访问的方法了

一、公有继承和虚函数

//"header.h
#ifndef HEADER_H_
#define HEADER_H_
#include<iostream>
#include<string>
#pragma warning(disable:4996)
using namespace std;

class T
{
private:
	string name;
	bool hasTable;
protected:
//	void pp() { cout << "protested" << endl; }
public:
	T(const string& n = "none", bool ht = false);
	void Name() const { cout << name << endl; }
	bool HasTable()const { return hasTable; }

	void staicBinding() const { cout << "T\n"; }
	virtual void show() const;
	virtual ~T() {} //虚析构函数
};

class RT :public T//公有派生
{
private:
	unsigned int rating;
public:
	RT(unsigned int r = 0, const string& n = "none", bool ht = false);
	RT(unsigned int, const T&);
	int Rating()const { return rating; }

	void staticBinding() const { cout << "RT\n"; }
	//virtual void show() const;
	void show() const; //派生类中加不加 virtual 都行,派生类的派生类也是如此。只需要在基类中添加virtual即可
	//virtual ~RT() {} //同理,有没有都可以。

	virtual void v() const {}

	friend void Dss(RT& t) {
		cout << t.rating << endl;
	}
	friend void sss(T& t) {
		t.Name();
	}
};
#endif
//"other.cpp"
#include "header.h"
#include<iostream>
T::T(const string& n,
	bool ht)
{
	name = n; // 调用string的构造函数,然后采用赋值运算符赋给 name
	hasTable = ht;
}
void T::show() const {
	cout << name << ", " << hasTable << endl; 
}


//列表初始化调用基类的构造函数,如果程序不调用基类构造函数,程序默认使用默认的基类函数( 等同于  :TableTennisPlayer() )。
RT::RT(unsigned int r, const string& n, bool ht) :T(n, ht)
{
	rating = r;
}
//调用基类的复制构造函数。在此采用默认的复制构造函数
RT::RT(unsigned int r, const T&
	a) : T(a)
{
	rating = r;
}
void  RT::show() const {
	cout << rating << ", ";
	T::show();
}

#include "header.h"
#include<iostream>
void sss(T&);
int main()
{
	using std::cout;
	using std::endl;
	T t1("bbb", false);
	RT rt1(1140, "aaa", true);
	// 基类指针或引用只能调用基类方法,不能调用派生类方法 
	rt1.Name();
	rt1.Rating();
	t1.Name();

	
	RT rt2(1212, t1);
	T* pt = &rt1; 
	// 静态联编 (static binding) 
	pt->staicBinding();  // T::staicBinding()
	
	T t = rt2; 
	// 采用隐式重载赋值运算符:TableTennisPlayer& operator=(const TableTennisPlayer&) const;
	// 基类引用指向一个 派生类对象, 将派生类的基类对象复制给基类。

	// 虚函数: 动态联编 (dynamic binding) 
	t1.show();
	rt2.show();
	T* tt = &rt2;
	tt->show();   // RT::show()
	tt->staicBinding(); // T::staicBinding()

	//virtual: 采用了virtual的方法,被指针/引用 调用时,根据其具体指向的类型来调用对应的方法
	//否则,根据指针/引用 的类型来调用方法

	T* rt = new RT(2, "haha", true);
	delete rt; // 因为前面采用了虚析构函数,所以按照动态联编的规则,
	//动态调用 RT 的(默认)析构函数。
	
	//虚函数表:
	//每个对象内部含有一个隐藏成员,其保存了一个指向数组的指针,这个数组里保存的是当前对象的虚函数地址。
	//该数组称为虚函数表。
	//如:T对象的虚函数表中只有一项(因为 T 中只有一个虚函数): 一个指向 T::show() 的地址,
	//假设该地址为 0x1;
	//而RT对象的虚函数表有两项:
	//一个是 RT::show() 的地址,这个地址和T::show() 肯定不一样,假设该值为 0x2;
	//另一个是指向虚函数 RT::v() 的地址值,该地址值假设为0x3。
	
	//在执行 T* tt = &rt2; tt->show(); 时,程序所查找的是RT::show()的地址 0x2,然后执行。
	//虚函数效率较低,因为其多了一项查表的行为。

	Dss(rt1);
	sss(t1);//不可访问,因为 sss 是 RT的友元函数,而它的参数又不是 RT 类型,所以无法找到 sss 函数的定义。 添加全局声明即可
	return 0;

}

      公有继承/派生:
      存储了基类的数据成员(派生类继承了基类的实现);
      派生类对象可以使用基类的方法(派生类继承了基类的接口)
      在派生类(类方法实现)中可以访问基类的公有方法和保护方法。

      创建派生类对象时,程序首先调用基类的构造函数,然后调用派生类的构造函数。析构时刚好相反。


      虚函数:
      友元函数不能是虚函数,只有类成员才能是虚函数
      没有重新定义虚函数,会使用离它最近的基类的虚函数版本
      如果重新定义一个同名方法,但是参数列表不同,就会隐藏了同名的基类的虚函数。 但是返回值可以由返回基类的指针/引用变成返回派生类的指针/引用(返回类型协变)。如果基类中的虚函数发生重载(存在多个同名的虚函数),那么在派生类中需要定义所有重载的虚函数(不然基类中的重载的一些函数会被隐藏,派生类汇中无法进行访问)。

二、抽象基类


#include<iostream>
#include<string>
using std::string;
using std::cout;
using std::endl;
class A
{
private:
	string name;
	long id;
protected:
	const string& getName() const { return name; }
	long getId() const { return id; }
public:
	A(const string& n = "haha", long i = -1) :name(n), id(i) {}
	//virtual void show() = 0; // 虚函数使用 =0 表示其为一个纯虚函数
	//含有至少一个纯虚函数,则该类为抽象基类,无法创建其对象。

	virtual void show() = 0 { // 抽象基类也可以有函数定义
		cout << name << ", " << id << endl;
	}
	virtual ~A() {};
};
class B :public A
{
private:
	double height;
public:
	B(const string& s = "haha", long i = -1, double h = 180.1) :A(s, i), height(h) {}
	virtual void show() {
		A::show();
		cout << height << ", " << getName() << ", " << getId() << endl;
	}
	virtual ~B() {};
};
class C :public A
{
private:
	double weight;
public:
	C(const string& s = "Nullbody", long i = -1, double w = 100.2) :A(s, i), weight(w) {}
	virtual void show() {
		cout << weight << ", " << A::getName() << ", " << A::getId() << endl;
	}
	void setW(double a) { weight = a; }
};


int main()
{
	using namespace std;
	//A a;
	B b;
	C c;
	b.show();
	c.show();
	return 0;
}

三、动态内存分配


#include<iostream>
using namespace std;
class BD
{
private:
	char* label;
	int rating;
public:
	BD(const char* l = "null", int r = 0);
	BD(const BD&);
	virtual ~BD();
	BD& operator=(const BD&);
	friend std::ostream& operator<<(std::ostream&, const BD&);
};
class D :public BD   // 如果派生类中存在动态内存分配,那么需要显式定义 构造函数,析构函数,复制构造函数和赋值运算符
{
private:
	char* style;
public:
	D(const char* l = "null", int r = 0, const char* s = "none");
	D(const char* s, const BD&);
	D(const D&);
	~D();
	D& operator=(const D&);
	friend std::ostream& operator<<(std::ostream&, const D&);
};

BD::BD(const char* l, int r)
{
	int temp = strlen(l);
	label = new char[temp + 1];
	strcpy_s(label, temp + 1, l);
	rating = r;
}
BD::BD(const BD& a)
{
	rating = a.rating;
	int temp = strlen(a.label);
	label = new char[temp + 1];
	strcpy_s(label, temp + 1, a.label);
}
BD::~BD()
{
	delete[]label;
}
BD& BD::operator=(const BD& a)
{
	if (&a == this)
		return *this;
	rating = a.rating;
	int temp = strlen(a.label);
	delete[] label;
	label = new char[temp + 1];
	strcpy_s(label, temp + 1, a.label);
	return *this;
}
ostream& operator<<(ostream& os, const BD& a)
{
	os << "Label: " << a.label << ", " << a.rating << endl;
	return os;
}
D::D(const char* l /* = "null" */, int r /* = 0 */,
	const char* s /* = "none" */) :BD(l, r)
{
	int temp = strlen(s);
	style = new char[temp + 1];
	strcpy_s(style, temp + 1, s);
}
D::D(const char* s, const BD& a) :BD(a)
{
	int temp = strlen(s);
	style = new char[temp + 1];
	strcpy_s(style, temp + 1, s);
}
D::D(const D& s) :BD(s)
{
	int temp = strlen(s.style);
	style = new char[temp + 1];
	strcpy_s(style, temp + 1, s.style);
}
D::~D()
{
	delete[] style;
}
D& D::operator=(const D& a)
{
	if (&a == this)
		return *this;
	BD::operator=(a);
	delete[] style;
	style = new char[strlen(a.style) + 1];
	strcpy_s(style, strlen(a.style) + 1, a.style);
	return *this;
}
ostream& operator<<(ostream& os, const D& a)
{
	os << (const BD&)a;
	os << "Style: " << a.style << endl;
	return os;
}
int main()
{
	BD shirt("BD", 8);
	D map("BD", 5, "D");
	cout << "Displaying D object: \n";
	cout << shirt << endl;
	cout << "Displaying D object: \n";
	cout << map << endl;

	D map2;
	map2 = map;
	cout << map2 << endl;
	return 0;
}

四、包含和私有继承

      is-a :公有继承,派生类中可以访问基类的公有/保护成员,它们是派生类的公有成员。基类的接口是派生类的公有接口,外界可以访问。

      has-a:
      包含,派生类中可以访问基类的公有/保护成员,外界不能通过派生类访问基类的公有成员。
      私有继承,派生类中可以访问基类的公有/保护成员,它们是派生类的私有成员。基类的接口是派生类私有接口,外界不可以访问。
      包含中,相当于有一个有名字的基类对象,而在私有继承中,相当于有一个没有命名的基类对象。

包含

// header.h
#ifndef HEADER_H_
#define HEADER_H_
#include<iostream>
#include<vector>
using namespace std;
#include<string>
class Student
{
private:
	vector<double> scores;
	string name;
	ostream& arr_out(ostream&) const;
public:
	Student() :name("Null student"), scores() {}
	explicit Student(const string& a) :name(a), scores() {} //为了避免隐式构造函数将 string 作为参数创建出一个 Student 对象
	explicit Student(int& a) :name("Nully"), scores(a) {} //同上
	Student(const string& a, int b) :name(a), scores(b) {}
	Student(const string& a, const vector<double>& b) :name(a), scores(b) {}
	Student(const char* a, double* b, int n) :name(a) {
		for (int i = 0; i < n; i++)
			scores.push_back(b[i]);
	}
	~Student() {};
	double Average() const;
	const string& Name() const;
	double& operator[](int i);
	double operator[](int i) const;

	friend istream& operator>>(istream&, Student&);
	friend istream& getline(istream&, Student&);
	friend ostream& operator<<(ostream&, const Student&);
};
#endif

//other.cpp
#include "header.h"
double Student::Average()const {
	if (scores.size() == 0)
		return 0;
	double sum = 0;
	for (double d : scores)
		sum += d;
	return sum / scores.size();
}
const string& Student::Name() const {
	return name;
}
double& Student::operator[](int i)
{
	return scores[i];
}
double Student::operator[](int i) const {
	return scores[i];
}

ostream& Student::arr_out(ostream& os) const {
	int i;
	int lim = scores.size();
	if (lim){
		for (i = 0; i < lim; i++)
			os << scores[i] << ' ';
		cout << endl;
	}
	else
		os << " empty array!\n";
	return os;
}
istream& operator>>(istream& is, Student& a){
	is >> a.name;
	return is;
}
istream& getline(istream& is, Student& a){
	getline(is, a.name);
	return is;
}
ostream& operator<<(ostream& os, const Student& a){
	os << a.Name() << endl;
	a.arr_out(os);
	return os;
}

私有继承

//header.h
#ifndef HEADER_H_
#define HEADER_H_
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class Student :private string, private vector<double>
{
private:
	typedef vector<double> v;
	ostream& arr_out(ostream&) const;
public:
	using string::size;
	Student() :string("Null student"), v() {}
	explicit Student(const string& a) :string(a), v() {}
	explicit Student(int a) :string("Nully"), v(a) {}
	Student(const string& a, int b) :string(a), v(b) {}
	Student(const string& a, const v& b) :string(a), v(b) {}
	Student(const char* a, double* b, int n) :string(a) {
		for (int i = 0; i < n; i++)
			v::push_back(b[i]);
	}
	~Student() {};
	double Average() const;
	const string& Name() const;
	double& operator[](int i);
	double operator[](int i) const;

	friend istream& operator>>(istream&, Student&);
	friend istream& getline(istream&, Student&);
	friend ostream& operator<<(ostream&, const Student&);
};
#endif

//other.cpp
#include "header.h"
double Student::Average()const {
	if (v::size() == 0)
		return 0;
	double sum = 0;
	for (double d : (v)*this)
		sum += d;
	return sum / v::size();
}
const string& Student::Name() const {
	return (const string&)*this;
}
double& Student::operator[](int i)
{
	return v::operator[](i);
}
double Student::operator[](int i) const {
	return v::operator[](i);
}

ostream& Student::arr_out(ostream& os) const {
	int i;
	int lim = v::size();
	if (lim){
		for (i = 0; i < lim; i++)
			os << v::operator[](i) << ' ';
		cout << endl;
	}
	else
		os << " empty array!\n";
	return os;
}
istream& operator>>(istream& is, Student& a){
	is >> (string&)a;
	return is;
}
istream& getline(istream& is, Student& a){
	getline(is, (string&)a);
	return is;
}
ostream& operator<<(ostream& os, const Student& a){
	os << (const string&)a<< endl;// 调用基类的友元函数
	a.arr_out(os);
	return os;
}

      1,采用作用域解析符在类中访问基类的公有、保护成员
      2,通过强制类型转换,访问基类对象
      3,访问基类的友元函数时,在派生类的友元函数中,将参数从派生类强制类型转换为基类类型。

      一般情况下,应该使用包含。在需要访问基类的保护成员时,或者需要重新定义虚函数时,应该使用私有继承。

      保护继承:派生类中可以访问基类的公有/保护成员,它们是派生类的保护成员。基类的接口是派生类保护接口,外界不可以访问。它在私有继承的区别在于第三代类中可以使用基类的公有/保护成员

      如果想在外界访问私有/保护继承中的基类方法:1,在派生类中重新定义一个公有方法(其中调用了想访问基类方法)2,在派生类的公有成员中,采用using声明,仅仅只需要使用成员名即可。如:using string::size;

五、多重公有继承

#include<iostream>
#include<string>
using namespace std;
class A  //抽象基类
{
private:
	string name;
public:
	A() :name("no one") {}
	A(const string& s) :name(s) {}
	virtual ~A() = 0 {} //纯虚函数
	virtual void show() const;
};
class B : public A
{
private:
	int id;
public:
	B() :A(), id(0) {}
	B(const string& s, int b = 0) :A(s), id(b) {}
	B(const A& a, int b = 0) :A(a), id(0) {}
	void show() const;
};
class C : public A
{
private:
	double income;
public:
	C() :A(), income(0) {}
	C(const string& s, double b = 0) :A(s), income(b) {}
	C(const A& a, double b = 0) :A(a), income(b) {}
	void show() const;
};

//A::~A() {}

void A::show() const {
	cout << "Name: " << name << '\n';
}
void B::show() const {
	cout << "ID " << id << ", ";
	A::show();
}
void C::show() const {
	cout << "Income: " << income << ", ";
	A::show();
}

int main()
{
	B b("b", 5);
	C c("c", 3.14);
	B bb;
	C cc;

	A* pw[] = { &b, &c, &bb,&cc };
	for (int i = 0; i < 4; i++)
		pw[i]->show();
	return 0;
}

      假设一个类 D 继承 B 和 C,如:class D:public B,public C{…};
      那么一个D对象中将包含两个A对象,B中一个,C中一个。那么在使用A指针指向D时,不能直接写成:A* p=&D; 而是应该写成 A* p=(B*)&D; 或者A* p=(C*)&D;
      为了避免在D对象中包含两个A对象,采用虚基类可以使得派生类只继承一个基类(A)对象。如下:

虚基类

#include<iostream>
#include<string>
using namespace std;
class A  //抽象基类
{
private:
	string name;
public:
	A() :name("no one") {}
	A(const string& s) :name(s) {}
	virtual ~A() = 0 {} //纯虚函数
	virtual void show() const;
};
class B : virtual public A
{
private:
	int id;
public:
	B() :A(), id(0) {}
	B(const string& s, int b = 0) :A(s), id(b) {}
	B(const A& a, int b = 0) :A(a), id(0) {}
	void show() const;
	int get()const { return id; }
};
class C : public virtual A
{
private:
	double income;
public:
	C() :A(), income(0) {}
	C(const string& s, double b = 0) :A(s), income(b) {}
	C(const A& a, double b = 0) :A(a), income(b) {}
	void show() const;
	double get()const { return income; }
};
class D : public B, public C
{
public:
	D() {};
	// 构造函数中必须先初始化抽象基类 A,然后在初始化虚基类 B 和 C
	D(const string& s, int p = 0, double v = 0)
		:A(s), B(s,p), C(s,v) {}
	D(const A& a, int p = 0, int v = 0)
		:A(a), B(a, p), C(a, v) {}
	D(const B& b, int v = 0)
		:A(b), B(b), C(b, v) {}
	D(const C& c, int p = 0)
		:A(c), B(c, p), C(c) {}
	void show() const;
};

void A::show() const {
	cout << "Name: " << name << '\n';
}
void B::show() const {
	cout << "ID " << id << ", ";
	A::show();
}
void C::show() const {
	cout << "Income: " << income << ", ";
	A::show();
}
void D::show() const {
	cout << "ID " << B::get() << ", ";
	cout << "Income: " << C::get() << ", ";
	A::show();
}
int main()
{
	B b("b", 5);
	C c("c", 3.14);
	B bb;
	C cc;

	A* pw[] = { &b, &c, &bb,&cc };
	for (int i = 0; i < 4; i++)
		pw[i]->show();
	D d;
	d.show();
	return 0;
}

六、模板类

      模板类中关于成员函数模板定义有三种方法,一种是在类中定义,另一种是在类外同一个文件中定义,最后一种是在类外不同文件中定义。

第一种,类中定义:

       header.hpp:

#pragma once

#include<iostream>
using namespace std;

template <class Type>// 模板类
class Gen
{
private:
	int top;
public:
	void show(){
		Type tmp = "abcd";
		cout<< tmp<<endl;
	}
};

       main.cpp:

#include "header.hpp"
using namespace std;

int main(){
	Gen<string> gen;
	gen.show();
	return 0;
}

第二种,类外同一个文件定义:

       header.hpp:

#include<iostream>
using namespace std;

template <typename Type>// 模板类
class stack
{
private:
	enum { max = 10 };
	Type arr[max];
	int top;
public:
	stack(int a=max) :top(0),max(a) {}//默认构造函数
	void push(const Type& a);
	void pop(Type& a);//
};

template<typename Type> // 类定义中的函数都是模板函数
void stack<Type>::push(const Type& a){
	arr[top++] = a;
}
template<typename Type>
void stack<Type>::pop(Type& a){
	a = arr[--top];
}

       main.cpp:

#include "header.hpp"
using namespace std;

int main()
{
	stack<string> st; //数组大小为默认的 10;
	st.push("hhhh");
	string a("oo");
	st.pop(a);
	cout << a << endl;
	stack<string>a(5);//修改数组大小为 5
	
	// using 等价于 typedef
	using ss = stack<string>;
	using si = stack<int>;
	using I = int;
	return 0;
}

第三种,类外不同文件中定义:

       header.hpp:

#pragma once

#include<iostream>

template <class Type>// 模板类
class Gen
{
private:
	int top;
public:
	void show();
};

       header.cpp中的模板函数需要采用如下写法。

#pragma once

#include "header.hpp"
using namespace std;

template <class Type>
void Gen<Type>::show(){
	Type tmp = "abcd";
	cout<< tmp<<endl;
}

       main.cpp 中的需要添加语句 #include "header.cpp" 否则在链接时会报错(找不到 show() 所属类的引用):

#include "header.hpp"
#include "header.cpp"

int main(){
	Gen<std::string> gen;
	gen.show();
	return 0;
}

七、友元类

#include<iostream>
using namespace std;
class Tv
{
private:
	int state;
	enum { Off, On };
	void onoff();
public:
	friend class Remote; //友元类声明的所在位置在 哪都可以(公有,私有,保护)
	Tv(int s = Off ) :state(s){}
	bool ison() const{ return state == On; }
};
void Tv::onoff() {
	state = (state == On) ? Off : On;
}
class Remote
{
public:
	Remote(){}
	void onoff(Tv& t) { t.onoff(); }
};

int main(){
	Tv s42;
	cout << s42.ison() << endl;
	Remote grey;
	grey.onoff(s42);
	cout << s42.ison() << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值