重庆分公司,新征程启航

为企业提供网站建设、域名注册、服务器等服务

C++学习Day.5(进入正轨~~)-创新互联

类和对象:   至此,您探索了简单的程序。这种程序从 main( ) 开始执行,包含局部变量、全局变量和常量,并 将执行逻辑划分为可接受参数和返回值的函数。前面使用的都是过程性编程风格,未涉及面向对象。   换句话说,您需要学习 C++ 面向对象(知人善用是为面向对象)编程基本知识。(终于开始了~) 1.声明类   要声明类,可使用关键字 class ,并在它后面依次包含类名,一组放在 {} 内的成员属性和成员函数,以及结尾的分号。   类声明将类本身及其属性告诉编译器。类声明本身并不能改变程序的行为,必须要使用类,就像 需要调用函数一样。   模拟人类的类类似于下面这样(请暂时不要考虑其中的语法):
class Human
{
// Member attributes:
string name;
string dateOfBirth;
string placeOfBirth;
string gender;
// Member functions:
void Talk(string textToTalk);
void IntroduceSelf();
...
};

创新互联建站服务项目包括邢台县网站建设、邢台县网站制作、邢台县网页制作以及邢台县网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,邢台县网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到邢台县省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!   不用说, IntroduceSelf( ) 将使用 Talk( ) 以及封装在类 Human 中的一些数据。通过关键字 class , C++提供了一种功能强大的方式,让您能够创建自己的数据类型,并在其中封装属性和使用它们的函数。   类的所有属性(这里是 name 、 dateOfBirth 、 placeOfBirth 和 gender )以及在其中声明的函数( Talk( ) 和IntroduceSelf( ))都是类( Human )的成员。   封装指的是将数据以及使用它们的函数进行逻辑编组,这是面向对象编程的重要特征。   另外,您可能经常遇到术语“方法”,它其实指的就是属于类成员的函数。

  类相当于蓝图,仅声明类并不会对程序的执行产生影响。在程序执行阶段,对象是类的化身。要 使用类的功能,通常需要创建其实例 — 对象,并通过对象访问成员方法和属性。

我们也可以使用new来为对象动态的申请内存。

Human* firstWoman = new Human(); // dynamically allocated Human 
delete firstWoman; // de-allocating memory

注:即Human是申明的类,其firstWoman是对象

  类声明表明, firstMan 有 dateOfBirth 等属性,可使用句点运算符(.)来访问:  
firstMan.dateOfBirth = "1970";
   这是因为从类声明表示的蓝图可知,属性 dateOfBirth 是类 Human 的一部分。仅当实例化了一个对象后,这个属性在现实世界(运行阶段)才存在。句点运算符(.)用于访问对象的属性。 这也适用于 IntroduceSelf( ) 等方法:
firstMan.IntroduceSelf();

如果有一个指针 firstWoman,它指向 Human 类的一个实例,则可使用指针运算符(->)来访问成员(这将在下一小节介绍),也可使用间接运算符(*)来获取对象,再使用句点运算符来访问成员:

Human* firstWoman = new Human();
(*firstWoman).IntroduceSelf();

总的来说和结构体长得差不多的,目前而言。

关键字:public和private   信息至少可以分两类:不介意别人知道的数据和保密的数据。对大多数人来说,性别就是一项不 介意别人知道的信息,但收入可能属于隐私。   C++ 让您能够将类属性和方法声明为公有的,这意味着有了对象后就可获取它们;也可将其声明 为私有的,这意味着只能在类的内部(或其友元)中访问。作为类的设计者,您可使用 C++ 关键字 public 和 private 来指定哪些部分可从外部(如 main( ) )访问,哪些部分不能。

样例(没有默认构造函数的类):

#include#include#include
#includeusing namespace std;

class Human {
	private:
		int age;
		string name;
	public:
		int GetAge(){
			return age;
		}
		void SetAge(int humansAge){
			if(humansAge>0)
			age=humansAge;
			//避免被设置为0
		}
};
int main(void) {

	Human eve;
	eve.SetAge(16);
	cout<

同样,我们可以简单更改程序,使得其输出更小的年龄。 

之所以我们可以达成这一目的,是由于age被声明在private中,不能直接访问它,只能通过GetAge方法进行访问。

  在面向对象编程语言中,抽象是一个非常重要的概念,让程序员能够决定哪些属性只能让类及其成员知道,类外的任何人都不能访问( 友元除外 )
构造函数:

1.声明和实现构造函数

  构造函数是一种特殊的函数,它与类同名且不返回任何值。因此, Human 类的构造函数的声明类

似于下面这样:

class Human
{
public:
Human(); // declaration of a constructor
};
  这个构造函数可在类声明中实现,也可在类声明外实现。在类声明中实现(定义)构造函数的代

码类似于下面这样:

class Human
{
public:
Human()
{
// constructor code here
}
};

在类声明外定义构造函数的代码类似于下面这样:

class Human
{
public:
Human(); // constructor declaration
};
// constructor implementation (definition)
Human::Human()
{
// constructor code here
}
注        ::被称为作用域解析运算符。例如,Human::dateOfBirth 指的是在 Human 类中声明的变量 dateOfBirth ,而 ::dateOfBirth 表示全局作用域中的变量 dateOfBirth 。
何时,如何使用构造函数及重载构造函数:  注:  可在不提供参数的情况下调用的构造函数被称为默认构造函数。 默认构造函数是可选的。 如果您 没有提供默认构造函数,编译器将为您创建一个,这种构造函数会创建成员属性,但不会将 POD 类型(如 int )的属性初始化为非零值。如上一张完整程序图    而如果我们创建了默认构造函数,那么成员属性会初始化为0.

构造函数的使用代码举例:

#include#includeusing namespace std;

class Human {
	private:
		int age;//init 0
		string name;
	public:
	Human(){
		age=0;
		cout<< "Default constructor: name and age not set."<

您可选择不实现默认构造函数,从而要求实例化对象时必须提供某些参数,也就是删去上代码main中的
Human(){
		age=0;
		cout<< "Default constructor: name and age not set."<

带有默认值的构造函数:

class Human 
{ 
private: 
 string name; 
 int age; 
public: 
 // overloaded constructor (no default constructor) 
 Human(string humansName, int humansAge = 25) 
 { 
 name = humansName; 
 age = humansAge; 
 cout<< "Overloaded constructor creates "<< name; 
 cout<< " of age "<< age<< endl; 
 } 
 // ... other members 
};
Human adam("Adam"); // adam.age is assigned a default value 25 
Human eve("Eve", 18); // eve.age is assigned 18 as specified
注意:    默认构造函数是调用时可不提供参数的构造函数,而并不一定是不接受任何参数的构造函数。因此,下面的构造函数虽然有两个参数,但它们都有默认值,因此也是默认构造函数:
class Human
{
private:
string name;
int age;
public:
// default values for both parameters
Human(string humansName = "Adam", int humansAge
= 25)
{
 name = humansName; 
 age = humansAge; 
 cout<< "Overloaded constructor creates "; 
 cout<< name<< " of age "<< age; 
 } 
};

buffer:n. 缓冲物,起缓冲作用的人;缓冲器;缓存区;老糊涂 v. 保护,保障;缓存

析构函数:

在对象被销毁时自动调用,释放内存(在main函数之后)。

当然,析构函数还是比较困难的,在我们学习的初期,先对它进行了解即可,而无需深研。

声明和实现析构函数

  析构函数看起来像一个与类同名的函数,但前面有一个腭化符号( ~ )。因此, Human 类的析构函数的声明类似于下面这样:
class Human
{ 
 ~Human(); // declaration of a destructor 
};

在类中:

class Human 
{ 
public: 
 ~Human() 
 { 
 // destructor code here 
 } 
};

在类外:

class Human 
{ 
public: 
 ~Human(); // destructor declaration 
}; 
// destructor definition (implementation) 
Human::~Human() 
{ 
 // destructor code here 
}
  正如您看到的,析构函数的声明与构造函数稍有不同,那就是包含腭化符号( ~ )。并且,析构函数的作用与构造函数完全相反。 何时及如何使用析构函数   每当对象不再在作用域内或通过 delete 被删除进而被销毁时,都将调用析构函数。这使得析构函 数成为重置变量以及释放动态分配的内存和其他资源的理想场所。 使用 char*缓冲区时,您必须自己管理内存分配和释放,因此本书建议不要使用它们,而使用 std::string。std::string 等工具都是类,它们充分利用了构造函数和析构函数,还有将在第 12 章介绍的运算符,让您无需考虑分配和释放等内存管理工作。 程序清单 9.7 所示的类 MyString 在构造函数中为一个字符串分配内存,并在析构函数中释放它。

注:

  析构函数不能重载,每个类都只能有一个析构函数。如果您忘记了实现析构函数,编译 器将创建一个伪( dummy )析构函数并调用它。伪析构函数为空,即不释放动态分配的 内存。

腭化符号(~):main函数运行结束后,令之自动调用。

放松时刻:

#include#includeusing namespace std;

class Human {
	private:
		int age;//init 0
		string name;
	public:
	Human(string humansName="Adam",int humansAge=25)
	:name(humansName),age(humansAge)
	{
//		name=humansName;
//		age=humansAge;
		cout<< "Overloaded constructor creates ";
		cout<

continue:

复制构造函数:

浅复制带来的问题: 

分析:

    在程序清单 9.7 中运行正常的 MyString 类,为何会导致程序清单 9.8 崩溃呢?相比于程序清单 9.7 ,程序清单 9.8 唯一不同的地方在于,在 main( ) 中,将使用 MyString 对象 sayHello 的工作交给了函数UseMyString(),如第 44 行所示。在 main( ) 中将工作交给这个函数的结果是,对象 sayHello 被复制到形参 str ,并在 UseMyString( ) 中使用它。编译器之所以进行复制,是因为函数 UseMyString( ) 的参数 str 被声明为按值(而不是按引用)传递。对于整型、字符和原始指针等 POD 数据,编译器执行二进制复制,因此 sayHello.buffer 包含的指针值被复制到 str 中,即 sayHello.buffer 和 str.buffer 指向同一个内存单元。     二进制复制不复制指向的内存单元, 这导致两个 MyString 对象指向同一个内存单元 。函数 UseMyString( ) 返回时,变量 str 不再在作用域内,因此被销毁。为此,将调用 MyString 类的析构函数,而该析构函数使用 delete[] 释放分配给 buffer 的内存(如程序清单 9.8 的第 22 行所示)。这将导致 main( ) 中的对象 sayHello 指向的内存无效,而等 main( ) 执行完毕时, sayHello 将不再在作用域内,进而被销毁。但这次第 22 行对不再有效的内存地址调用 delete (销毁 str 时释放了该内存,导致它无效)。正是这种重复调用 delete 导致了程序崩溃。

使用复制构造函数确保深复制
//深复制的代码

好了,我没写 ,原因如下: 

寄,太难了,看不懂了,构造函数和析构函数的其他用途就不写了,蒟蒻感juo无能为力了。

往下走

this 指针     在 C++ 中,一个重要的概念是保留的关键字 this 。在类中,关键字 this 包含当前对象的地址,换句话说,其值为&object 。当您在类成员方法中调用其他成员方法时,编译器将隐式地传递 this 指针 ———数调用中不可见的参数:
class Human 
{ 
private: 
 void Talk (string Statement) 
 { 
 cout<< Statement; 
 } 
public: 
 void IntroduceSelf() 
 { 
 Talk("Bla bla"); // same as Talk(this, "Bla Bla") 
 } 
};
  在这里,方法 IntroduceSelf( ) 使用私有成员 Talk( ) 在屏幕上显示一句话。实际上,编译器将在调用 Talk 时嵌入 this 指针,即 Talk(this, “Blab la”) 。   从编程的角度看, this 的用途不多,且大多数情况下都是可选的。例如,在程序清单 9.2 中,可将SetAge( )中访问 age 的代码修改成下面这样:
void SetAge(int humansAge) 
{ 
 this->age = humansAge; // same as age = humansAge 
}
将 sizeof( )用于类   您知道,通过使用关键字 class 声明自定义类型,可封装数据属性和使用数据的方法。第 3 章介绍过,运算符 sizeof( ) 用于确定指定类型需要多少内存,单位为字节。这个运算符也可用于类,在这种情况下,它将指出类声明中所有数据属性占用的总内存量,单位为字节。sizeof( ) 可能对某些属性进行填充,使其与字边界对齐,也可能不这样做,这取决于您使用的编译器。

结构和类的不同之处:

关键字 struct 来自 C 语言,在 C++ 编译器看来,它与类及其相似,差别在于程序员未指定时,默 认的访问限定符( public 和 private )不同。因此,除非指定了,否则 结构中的成员默认为公有的(而类成员默认为私有的) ;另外,除非指定了,否则 结构以公有方式继承基结构(而类为私有继承)

Human的struct实现:

struct Human 
{ 
 // constructor, public by default (as no access specified is mentioned) 
 Human(const MyString& humansName, int humansAge, bool humansGender) 
 : name(humansName), age (humansAge), Gender(humansGender) {}
 int GetAge () 
 { 
 return age; 
 } 
private: 
 int age; 
 bool gender; 
 MyString name; 
};
正如您看到的,结构 Human 与类 Human 很像;结构的实例化与类的实例化也很像:
Human firstMan("Adam", 25, true); // an instance of struct Human
声明友元 不能从外部访问类的私有数据成员和方法,但这条规则不适用于友元类和友元函数。要声明友元 类或友元函数,可使用关键字 friend

样例代码:

#include#includeusing namespace std;

class Human {
	private:
		friend class Utility;
		int age;//init 0
		string name;
	public:
		Human(string humansName = "Adam", int humansAge = 25)
			: name(humansName), age(humansAge) {
//		name=humansName;
//		age=humansAge;
		}
};

class Utility {
	public:
		static void DisplayAge(const Human& person) {
			cout<< person.age<< endl;
		}
};

int main(void) {

	Human firstMan("Adam", 25);
	cout<< "Accessing private member age via friend class: ";
	Utility::DisplayAge(firstMan);
	//Utility这个类没有对象,所以我们直接使用它的DisplayAge函数
	return 0;
}

friend class Utility;

指出 Utility 类是 Human 类的友元,该声明让 Utility 类的所有方法都能访问 Human 类的私 有数据成员和方法。

共用体:一种特殊的数据存储机制
共用体是一种特殊的类,每次只有一个非静态数据成员处于活动状态。因此,共用体与类一样,可包含多个数据成员, 但不同的是只能使用其中的一个 。

1.声明共用体

union UnionName 
{ 
 Type1 member1; 
 Type2 member2; 
…
 TypeN memberN; 
};

实例化:

UnionName unionObject; 
unionObject.member2 = value; // choose member2 as the active member

注:

与结构类似,共用体的成员默认也是公有的,但不同的是,共用体不能继承。 另外,将 sizeof() 用于共用体时,结果总是为共用体大成员的长度,即便该成员并不处 于活动状态。

2.何时使用共用体:

  在结构中,常使用共用体来模拟复杂的数据类型。共用体可将固定的内存空间解释为另一种类型,有些实现利用这一点进行类型转换或重新解释内存,但这种做法存在争议,而且可采用其他替代方式。

enum和switch-case一起用。

对类和结构使用聚合初始化

例1:

int myNums[] = { 9, 5, -1 }; // myNums is int[3]
char hello[6] = { 'h', 'e', 'l', 'l', 'o', ' \0' };
然而,并非只有由整数或字符等简单类型组成的数组属于聚合类型,类(以及结构和共用体)也 可能属于聚合类型。有关结构和类的规范标准指出了结构和类必须满足什么条件才属于聚合类型,但在不同的 C++ 标准中,需要满足的条件存在细微的差别,但完全可以这样说,即满足如下条件的类或结构为聚合类型,可作为一个整体进行初始化: 只包含公有和非静态数据成员,而不包含私有或受保护的数据成员;不包含任何虚成员函数;只涉及公有继承(不涉及私有、受保护和虚拟继承);不包含用户定义的构造函数。

例2:

struct Aggregate1
{
int num;
double pi;
};
//可将其作为一个整体进行初始化
Aggregate1 a1{ 2017, 3.14 };
再来看一个例子:  
struct Aggregate2
{
int num;
char hello[6];
int impYears[5];
};
//对于这个结构,可像下面这样进行初始化:
Aggregate2 a2 {42, {'h', 'e', 'l', 'l', 'o'}, {1998, 2003, 2011, 2014, 2017}};

问:总是应该编写一个复制构造函数吗? 答: 如果类的数据成员是设计良好的智能指针、字符串类或 STL 容器(如 std::vector ),则编译器 生成的默认复制构造函数将调用成员的复制构造函数。然而,如果类包含原始指针成员(如使用 int* 而不是 std::vector表示的动态数组),则需要提供设计良好的复制构造函数,确保将类对象按值传 递给函数时进行深复制,创建该数组的拷贝。

问:在复制构造函数中,为何将指向源对象的引用作为参数? 答: 这是编译器对复制构造函数的要求。其原因是,如果按值接受源对象,复制构造函数将调用 自己,导致没完没了的复制循环。

今天结束了,寄几寄~~~

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


当前标题:C++学习Day.5(进入正轨~~)-创新互联
当前链接:http://cqcxhl.com/article/dcigej.html