Review cpp day04

Posted 达少Rising

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Review cpp day04相关的知识,希望对你有一定的参考价值。

回顾:
Review cpp day01
Review cpp day02
Review cpp day03

十四、构造函数和初始化表

1、构造函数和普通的函数类似,也可以重载或带有缺省参数

05constuctor.cpp

#include <iostream>
using namespace std;

class Student{
public:
	Student(const string& name, int age = 0, int id = 0){
		cout << "构造函数1" << endl;
		m_name = name;
		m_age = age;
		m_id = id;
	}
	/*
	Student(const string& name){
		cout << "构造函数2" << endl;
		m_name = name;
		m_age = 0;
		m_id = 0;
	}
	*/
	void who(void){
		cout << "我叫" << m_name << "今年" << m_age 
			<< "岁,学号是" << m_id << endl;
	}
private:
	string m_name;
	int m_age;
	int m_id;
};
int main(void){
	//创建对象,构造函数将自动被调用
	Student s("张飞", 28,10011);
	s.who();
	Student s2(关羽");
	s2.who();
	return 0;
}

2、缺省(默认)构造函数

  • 1)如果类中没有定义任何的构造函数,编译器会为该类提供一个缺省(无参)构造函数
    • 对于基本类型的成员变量不做初始化
    • 对于类类型的成员变量,会自动调用相应类的无参构造函数来初始化。
      eg:
	class A{
	public:
		int m_i;//基本类型
		string m_str;//类类型的成员变量(成员子对象)
	};
	A a;//会自动调用无参构造函数创建a对象
	cout << a.m_i << endl;//未知结果
	cout << a.m_str << endl;//一定是空字符串

06defCons.cpp

#include <iostream>
using namespace std;

class A{
public:
	A(void){
		m_i = 1234;
	}
	int m_i;
};
class B{
public:	
	//编译器会给B类提供类似下面的缺省构造函数
	//B(void){}
	int m_j;//基本类型
	A m_a;//类类型(成员子对象)
};
int main(void){
	B b;
	cout << b.m_j << endl;//未知
	cout << b.m_a.m_i << endl;//1234
	return 0;
}
  • 2)如果类中自己定义了构造函数,无论是否有参数,编译器都不会再提供缺省的无参构造函数。

3、类型转换构造函数(单参构造函数)

class 目标类型{
	[explicit] 目标类型(源类型...){...}
};

注: 使用explicit关键字修改构造函数,可以强制要求这种转换必须显式地完成。

07castCons.cpp

#include <iostream>
using namespace std;

class Integer{
public:
	Integer(void){
		cout << "Integer::Interger(void)" << endl;
		m_data = 0;
	}
	//int-->Integer
	//类型转换构造函数
	explicit Integer(int data){
		cout << "Integer::Interger(int)" << endl;
		m_data = data;
	}
	void print(void){
		cout << m_data << endl;
	}
private:
	int m_data;
};
int main(void){
	Integer i;
	i.print();//0

	//发生隐式转换
	//1)Integer tmp(123)
	//2)i = tmp;
	i = 123;//加上explicit关键字后这里会出错
	i.print();//123

	//隐式转换可读性差,推荐显式转换
	//i = (Integer)321;//C风格
	i = Integer(321);//C++风格
	i.print();//321
	return 0;
}

4、拷贝(复制)构造函数

  • 1)用一个已存在的对象构造同类型的副本对象,会调用该类的拷贝构造函数:
	class 类名{
		类名(const 类名& ...){...}
	};

eg:

	class A{...};
	A a1;
	A a2(a1);

08cpCons.cpp

#include <iostream>
using namespace std;

class A{
public:
	A(int data = 0){
		cout << "A::A(int)" << endl;
		m_data = data;
	} 
	A(const A& that){
		cout << "A::A(const A&)" << endl;
		m_data = that.m_data;
	}
	int m_data;
};
int main(void){
	A a1(123);
	//A a2 = a1
	A a2(a1);//调用A类的拷贝构造函数
	cout << a1.m_data << endl;//123
	cout << a2.m_data << endl;//123
	return 0;
}
  • 2)如果每一个类没有自己定义拷贝构造函数,那么编译器会为该类提供一个缺省的拷贝构造函数:
    • 对于基本类型的成员变量,按字节复制;

    • 对于类 类型成员变量(成员子对象),会调用相应类的拷贝构造函数初始化。

      注: 一般情况不需要自己定义拷贝构造函数,因为编译器所提供的缺省拷贝构造函数已经很好用了。

09cpCons.cpp

#include <iostream>
using namespace std;

class A{
public :
	A(int data = 0){
		cout << "A::A(int)" << endl;
		m_data = data;
	} 
	A(const A& that){
		cout << "A::A(const A&)" << endl;
		m_data = that.m_data;
	}
	int m_data;
};

class B{
public:
	int m_i;//基本类型的成员变量
	A m_a;//类类型的成员变量(成员子对象)
};
int main(void){
	B b1;
	b1.m_i = 123;
	b1.m_a.m_data = 1234;
	B b2(b1);//调用B的拷贝构造函数
	cout << b1.m_i << ',' << b1.m_a.m_data << endl;//123,1234
	cout << b2.m_i << ',' << b2.m_a.m_data << endl;//123,1234
	return 0;
}
  • 3)拷贝构造函数调用时机

    • 用已存在的对象作为同类型对象的构造实参
      A a1;
      A a2(a1);
      //A a2 = a1;//和上面等价
    • 以对象形式向函数传递参数
      void foo(A a){}
      foo(a1);
    • 从函数中返回对象(有可能被编译器优化掉)
      A foo(void){
      A a;
      return a;
      }
  • 下面的代码调用了6次构造函数(包括无参和拷贝的构造函数)
    10cpCons.cpp

#include <iostream>
using namespace std; 

class A{
public:
	A(void){
		cout << "A::A(void)" << endl;
	}
	A(const A& that){
		cout << "A::A(const A&)" << endl;
	}
};
void func1(A a){}
A func2(void){
	A a;//无参
	cout << "&a" << &a << endl;
	return a;//拷贝
}
int main(void){
	A a1;//无参
	A a2 = a1;//拷贝
	func1(a1);//拷贝
	/*func2正常返回a拷贝到临时对象,然后再拷贝到a3.发生两次拷贝,但
	* 是因为编译器的优化,让a3直接引用a不再发生拷贝
	*/
	//去优化选项:g++  -fno-elide-constructors 10cpCons.cpp
	A a3 = func2();//拷贝
	cout << "&a3" << &a3 << endl;
	return 0;
}

5、初始化表

  • 1)语法
    class 类名{
    类名(形参表):成员变量(初值){//构造函数体}
    };

eg:

	class A{
	public:
		A(int i):m_i(i), m_i2(0){
			//m_i = i;
		}
		int m_i;
		int m_i2;
	};

01initlist.cpp

#include <iostream>
using namespace std;

class Student{
public:
	//先把成员变量定义出来,再赋初值	
	/*Student(const string& name, int age, int no){
		m_name = name;
		m_age = age;
		m_no = no
	}*/
	//定义成员变量时,同时初始化
	Student(const string& name, int age, int no)
		:m_name(name), m_age(age), m_no(no){}
	void who(void){
		cout << m_name << ',' << m_age << ',' << m_no << endl;
	}
private:
	string m_name;
	int m_age;
	int m_no;
};
int main(void){
	Student s("张飞", 28, 10011);
	s.who();
	return 0;
}
  • 2)多数情况下使用初始化表和在构造函数体中进行赋初值没有太大差异,可以任选一种方式,但是在某些特殊情况下必须要使用初始化表:
    • (1)如果有类 类型的成员变量,而该类又没有无参构造函数,则必须通过初始化表来初始化该变量。
      02initlist.cpp
#include <iostream>
using namespace std;

class A{
public:
	A(int data){
		cout << "A的构造函数"<< endl;
		m_data=data;
	}
	int m_data;
};

class B{
public:
	B(void):m_a(1234){
		cout << "B的构造函数" << endl;
	}
	A m_a;//类 类型成员变量
};
int main(void){
	B b;
	cout << b.m_a.m_data << endl;//1234
	return 0;
}
A的构造函数
B的构造函数
1234
  • (2)如果类中包含“const”或“引用”型的成员变量,必须要使用初始化表来初始化。
    const int num;
    num = 0;//这两个语句本身就是错误的,必须定义时同时初始化

03initlist.cpp

#include <iostream>
using namespace std;

int num = 100;

class A{
public:
	/*A(void){
		m_r = num;
		m_c = 200;
	}*/error
	A(void):m_r(num), m_c(200){}
	int& m_r;
	const int m_c;
};

int main(void){
	A a;
	cout << a.m_r << ',' << a.m_c << endl;
	return 0;
}

注: 成员变量的初始化的顺序由声明顺序决定,而与初始化表的顺序无关,不要用一个成员变量去初始化另一个成员变量。

04initlist.cpp

#include <iostream>
#include <cstring>
using namespace std;

class Dummy{
public:
	/*Dummy(const char* psz)
		:m_str(psz), m_len(m_str.size()){}*/
	Dummy(const char* psz)
		//:m_str(psz), m_len(strlen(psz)){}
		:m_str(psz ? psz:""), m_len(strlen(psz ?psz:"")){}
	//类的成员变量的初始化不是按初始化表的顺序,而是成员变量的声明顺序
	/*
	string m_str;
	size_t m_len;
	*/

	string m_str;
	size_t m_len;
};
int main(void){
	//Dummy d("hello world!");
	Dummy d(NULL);
	cout << d.m_str << "size=" << d.m_len << endl;
	return 0;
}
  • 练习:修改电子时钟类,增加计时器功能,要求使用初始化表

    • 1)如果以系统时间构造对象,表示时钟功能
    • 2)如果以无参构造对象,使用初始化表将时间初始化为0,表现为计时功能。
      05Clock.cpp
  • 扩展练习2.0:实现企业员工系统

    • 需求:修改和完善一个员工类
      • 1)为Employee增加构造函数(有参、无参)
      • 2)将类分成多文件实现:
        Employee.h//声明
        Employee.cpp//实现
      • *3)编写Makefile

Employee.h:

class Employee{
public:
	string m_name;
	int m_id;
private:
	double m_sarary;

public:
	Employee();
	Employee(int, string);
	void print(void);
	void countSarary(void);
};

Employee.cpp:

#include "Employee.h"
Employee::Employee(){
	cout << "Employee::Employee()" << endl;
}
Employee::Employee(int id, string m_name){
	cout << "Employee::Employee(int, string)" << endl;
	m_id = id;
	m_name = name;
}
void Employee::print(void){
	cout << m_id << "," << m_name << endl;
}
void Employee::countSarary(void){
	int days = 0;
	double fun_sarary = 0;
	double ex_sarary = 0;
	cout << "请输入出勤天数:";
	cin >> days;
		
	fun_sarary = 4000 * (days / 23.0);
	ex_sarary = fun_sarary / 2;
		
	m_sarary = fun_sarary + ex_sarary;
	cout << "总工资是:" << m_sarary << endl;
}

main.cpp:

int main(void){
	Employee e;
	return 0;
}

Makefile

a.out:Employee.o main.o
	g++  *.o
Employee.o:Employee.cpp
	g++ -c Employee.cpp
main.o:main.cpp
	g++ -c main.cpp
clean:
	rm *.o a.out 

以上是关于Review cpp day04的主要内容,如果未能解决你的问题,请参考以下文章

Review cpp day09

Review cpp day07

Review cpp day08

Review cpp day06

Review cpp day05

Review cpp day01