C++学习笔记3_类.和相关函数
1. 类
*在C++中,struct和class没有明显差别,不同C#,class一定要new(手动开辟内存)出来
struct Hero
{
char name[64];
int sex;
}
void print(struct Hero &h)
{
...
}
class AdvHero
{
public:
char name[64];
int sex;
void print()
{
cout<<name<<endl;
}
}
int main(void)
{
Hero h;
strcpy(h.name,"zhangsan");
//
AdvHero v;//不用new出来,c++中的new另有含义
strcpy(v.name,"lisi");
}
2. 构造函数:
class A
{
public:
int age;
char* Name;
A(int x)
{
age=x;
Name =(char*)malloc(100);
}
~A()
{
//析构函数没有形参,当对象被销毁前自动调用
if(name!=NULL)
{
free(name);
}
}
}
int mian()
{
A a(10);
}
//堆栈在a被声明时,在栈中,开辟存储a的内存,含有一个int大小的,和char*一个指针大小的空间,然后在堆中,开辟100个字节的空间,用Name指向它。
//但是析构的时候,只会自动清除栈的东西,所以一定要记得释放堆中的东西。
//栈是会被压的!!
拷贝构造函数:
class Test
{
//显式的拷贝构造函数,
Test(const Test &anthor)//const引用,就是不能改变anthor所代表的对象的任何东西。
{
//...
}
//=赋值操作符
void operator=(const Test &another)
{
//...
}
}
//和C#不同,C#是没有拷贝构造函数的。
//
int main(void){
Test t1;
Test t2(t1);
//这句话,如果=号操作没有重载,拷贝构造函数也没有重载,那么也相当于t2=t1;t1复制一份,给t2。
//就算Test没有传入参数为Test的构造函数,也能成功。能自动将成员值拷贝过去。
//因为有默认的拷贝构造函数
Test t3;
t3=t1;//这里就不是调用构造函数了,而是调用=操作函数
//
Test t4=func();//**这里也不会发生t4先构造,然后执行=操作,而是将func()返回的结果“转正”
//
t3=func();//**这里就会发生执行=操作了。而且func()返回的匿名对象会被析构掉。
}
******************************
//在C++中,struct和class没有明显差别。不同C#,class一定要new(手动开辟内存)出来。
//在classA a;这句中,已经调用了构造函数了。C#中,仅仅是声明一个空的引用变量a。
//在a=b;调用了操作符号=函数。C#中,则是引用变量a来引用b引用的对象,a和b都是引用同一个对象。
//所以C++中,对象的传参,参数返回(除非是返回堆中的对象引用),都是复制的操作,同时,复制的时候会调用默认的拷贝构造函数,出栈的时候,也会调析构函数。
//各种拷贝,析构,也是可以通过引用来优化的地方!
//这是C++和大多数语言的不同。
******************************
3. 深拷贝和浅拷贝
class Teacher
{
int m_id;
char*m_name;
public:
Teacher(int id,char *name)//这就算深拷贝了,浅拷贝的话,m_name的值和name是一样,指向同一块内存。
{
m_id=id;
int len = strlen(name);
m_name=(char*)malloc(len+1);//因为最后要以\0标志字符串结束,所以+1
strcpy(m_name,name);
}
//一般的浅拷贝构造函数
Teacher(const teacher&a)
{
mid=a.mid;
m_name=a.m_name;
}
//一般的浅拷贝构造函数
Teacher(const teacher&a)
{
//按Teacher(int id,char *name)来写。
}
~Teacher()
{
if(m_name!=NULL)
{
free(m_name);//记得释放堆上的内存
}
}
}
//*****free是不能重复对同一指针值(内存地址)执行两次的。
//*****所以,见到类中,有指针成员,一定要特别留意,先看析构函数,会不会有可能free同一个内存地址;然后考虑要不要深拷贝。
//*****浅拷贝风险,就是有指针成员时,析构时释放堆内存,可能会重复释放内存。
4. 构造函数的初始化列表
class B
{
public:
B(A &a1,A&a2,int m):m_a1(a1),m_a2(a2),m_m(m)//常量成员的赋值,只能放在初始化列表中
{
//如果这使用m_a1=a1,显然达不到调用A的构造函数的目的,只是调了A的=操作符函数
//如果这里写m_a1(a1),会语法错误,将m_a1当做了函数使用了
}
private:
A m_a1;
A m_a2;
int a=3;//很多编译器不支持这种写法,所以尽量要写到构造函数中
const int m_m;//只能赋值一次,而且不能const int m_m=3;因为有些编译器不支持
}
//在构造的时候,先执行A的构造两次,然后执行B构造函数体;在析构的时候,先析构m_a1和m_a2,然后再执行B析构的函数体。
5. 静态成员
void test()
{
int static a=10;//这句话只会在第一次执行的时候,给a赋值,因为a在运行初阶段就放在静态区了。
a++;
}
class A
{
public:
A()
{
//...
}
static int m_c;//也是在程序运行之初放在静态区的
}
**************************************************************
int AA::m_c=100;//在声明类的时候,就给静态变量赋初值了。
*记得#ifndef #endif
void funtion()
{
AA:m_c=100;//不用写成 int AA:m_c=100;这是在方法之外写的。
}
6. this指针
//对于一个类,只有非静态字段才属于这个对象(才在这个对象的内存中),static成员在静态区,而且方法也不在这个对象的内存区域中。
//那为什么a.getPrivateProperty()能获得a的私有成员呢,但是getPrivateProperty()方法不在a的内存区域内???
class Test
{
private:
int n1;
public:
Test(int i)
{
n1=i;
}
int getn1()
{
return n1;
//等价于return this->n1;
}
int getn2() const //加个const,防止这个方法内改变this所指向的内容,相当于const Test * const this(指向常量的常指针,相当于常引用了)
{
}
static void print()
{//...}
}
int main(void){
Test a(10);
a.getn1();
Test::Print();
}
通过编译后,变为:
struct Test
{
int n1;
};
void Test_initialize(struct Test *pThis,int i)
{
pThis->n1=i;
}
int Test_getn1(struct Test *pThis)
{
return pThis-<n1;
}
void Test_Print(){//.....}
int main{
Test a;
Test_initialize(&a,10);
Test_getn1(&a);
Test_Print();
}
//再次,在C++中,struct和class没有明显差别。
7. 返回对象本身,主要可以用于链式操作
class Test
{
private :
int a;
public:
&Text Add(Text &another)
{
this->a=this->a+another.a;
return *this;
}
}
//能达到每次连加都会改变自身,又不用复制。
8. 友元函数
class Point
{
private :
double x;
double y;
//
friend double PointDistance(const Point &p1,const Point &p2);
//也能friend double Calculater::PointDistance(const Point &p1,const Point &p2);
//Calculater类一定要在Point声明之前声明好
public:
Point(double x,double y)
{
this->x=x;
//...
}
double getX()
{
return this->x;
//以匿名的方式,返回x的复制值。
}
double getY(){ //...}
}
double PointDistance2(const Point &p1,const Point &p2)
{
//有getX(),getY()的操作,调用方法就有压栈出栈的过程,效率比p1.x低多了。
}
如果在类中声明有:
friend double PointDistance(const Point &p1,const Point &p2); 那么就可以p1.x,使用私有成员了。就像PointDistance是类中的成员函数一样。
**因为友元通常会破坏封装性,所以不推荐使用。
9. 声明。声明的作用,就是预先告知有这个东西,其地方要使用它,必须要有预先告知。比如:
double PointDistance2(const Point &p1,const Point &p2);
如果这个函数声明前,没有通过头文件,或者其他办法,获取到Point 的声明,那么编译不过的。
正确的办法:
class Point;//这个也是声明
double PointDistance2(const Point &p1,const Point &p2);
****这也是C#和C++不同的地方,C#是声明和实现放在一起,而且不分先后顺序。
10. 友元类
class B;
class A
{
friend class B;
priviate : int a;
}
class B
{
private : int b;
public:
B& addA(const A &a)
{
this->b= this->b+a.a;
return *this;
}
}
**因为友元通常会破坏封装性,所以不推荐使用。
11. 操作符重载
//
new和delete也是操作符,也能重载的
//
*通常会重载“=”号,用于深拷贝
*通常也会重载+-号,用于一些字段的加减
class Complex
{
private:
int a;
int b;
public:
Complex operator +(const Complex &b)
{
Complex c;
c.a=this->a+b.a;
c.b=this->b+b.a;
return c;
}
//如果写在外边的话Complex operator +(const Complex &a, const Complex &b); 调用也可以Complex c3= operator+(c1,c2);
Complex& operator +=(const Complex &b)
{
c.a=this->a+b.a;
c.b=this->b+b.a;
return this;
}
//后++运算,加const,防止拼命++下去
const Complex operator ++(int)//要有个int填坑
{
Complex temp(*this);
this.a++;
this.b++;
return temp;
}
//为了能cout << complex,但这里c1.operator<<(os);或者c1<<cout;太别扭
//所以只能写全局了
ostream & operator <<(ostream &os)
{
os<<this->a<<this->b;
return os;
}
}
ostream & operator <<(ostream &os,Complex &c);//这样就可以cout<<c了;
12. 任何传入类的字符串指针,都要考虑深拷贝和析构,否则如果外边释放了内存,那么使用会出错。
13. ***************************重写无参构造,=操作符必要的注意事项***************************
等号操作符注意事项:
class A
{
public:
char*Name;
A(const A &a)
{
this->name=new char[strlen(a.Name)];
if(this->name!=NULL){
strcpy(this->Name,a.Name);
}
}
//遇到有成员指针是开辟堆内存的,一定要重写=,重写无参构造,重写析构
A & operator=(const A &a)
{
if(this==*a) { return *a;}
//
if(this->name!=NULL)
{
delete[] this->name;
this->name = NULL;
}
//重新开辟
this->name=new char[strlen(a.Name)];
if(this->name!=NULL){
strcpy(this->Name,a.Name);
}
}
~A()
{
if(this->name==NULL)
{
delete[] this->name;
this->name = NULL;
}
}
}
14. []操作符重载,通常返回引用,因为a[i]应该既可以取,也可以修改才行。
char & operator[](const int &i)
{
return this->Name[i];
}
15. ()操作符重载
int main(void)
{
typeA a ;
a();//这里是由于操作符重载,所以可以这样
}
16. const引用和const函数
class A
{
public:
int getLenght1()//实际上会编译为 int getLenght(int *const this),const指针,指向不可变
{
}
//
int getLenght2() const //实际上为const int *const this,相当于const引用,指向内容不可变,指向不可变 , 相当于const A &a
{
}
}
// 不能通过const引用修改变量的值
void function(const A &a) //为了保护a不在方法中被改变
{
a.getLenght1(); //这里出错,用高安全的引用作为实参,调用低安全形参的函数。
a.getLenght2(); //这里才不会出错
}
17. 类模板
C#
public class Airport<T> where T:Plane //能限定T的类型
{
//..
}
template<class T> class Person
{
public:
T mId;
T mAge;
Person(T mld,TmAge);
template<class T> friend ostream& operator<<(ostream& os,Person<T> &p);//windows下
friend ostream& operator<<<T>(ostream& os,Person<T> &p);//linux下
}
//声明和实现分开
temple<class T> Person<T>::Person(T mld,TmAge)//十分麻烦的写法
{
}
//
template<class T> ostream& operator<<(ostream& os,Person<T> &p)
{
}
最好的写法:
//
template<class T> class Person;//先额外声明
template<class T>void Print(Person<T> &p);//先额外声明
template<class T> Person
{
public :
friend void Print<T>(Person<T> &p);
}
//
template<class T>void Print(Person<T> &p)
{
//....
}
//
继承
class SubPerson:public Person<int>
{
}
18. 类模板的声明部分和实现部分分离,然后使用的坑
//C++编译器,对cpp文件独立编译,不会看文件之间的关联有没有出错
//C++编译器在编译的时候,发现一个函数调用,在当前文件找不到,那么先留坑,让后面连接器找。
//模板类,会进行两次编译,第一次对模板;第二次,在发现调用的时候,再去找模板,再编译多一个具体的函数出来。
(include<头文件>只做内容替换,不编译)
//所以,函数模板的实现部分,一定要在使用前先编译好。那么:可以不包含头文件,直接包含源文件
#include "Person.cpp"
//还有业界的做法就是,不要声明和实现分离,将他们写到Person.hpp;那么别人就知道是模板了。
19. 模板类的静态成员
根据上边的结论
template <class A> class P
{
public:
static int a;
}
template <class A> P::a=1;
//
int main()
{
P<int> a;
P<char> b;
//a.a和b.a不是共享同一个a,因为由于模板的编译方式,P<int>和P<char>可以当做两个类
}
C++学习笔记3_类.和相关函数的更多相关文章
- Java学习笔记——File类之文件管理和读写操作、下载图片
Java学习笔记——File类之文件管理和读写操作.下载图片 File类的总结: 1.文件和文件夹的创建 2.文件的读取 3.文件的写入 4.文件的复制(字符流.字节流.处理流) 5.以图片地址下载图 ...
- APUE学习笔记3_文件IO
APUE学习笔记3_文件IO Unix中的文件IO函数主要包括以下几个:open().read().write().lseek().close()等.这类I/O函数也被称为不带缓冲的I/O,标准I/O ...
- python学习笔记4_类和更抽象
python学习笔记4_类和更抽象 一.对象 class 对象主要有三个特性,继承.封装.多态.python的核心. 1.多态.封装.继承 多态,就算不知道变量所引用的类型,还是可以操作对象,根据类型 ...
- Java学习笔记之---类和对象
Java学习笔记之---类和对象 (一)类 类是一个模板,它描述一类对象的行为和状态 例如:动物类是一个类,动物们都有属性:颜色,动物们都有行为:吃饭 public class Dog { Stri ...
- UML学习笔记:类图
UML学习笔记:类图 有些问题,不去解决,就永远都是问题! 类图 类图(Class Diagrame)是描述类.接口以及它们之间关系的图,用来显示系统中各个类的静态结构. 类图包含2种元素:类.接口, ...
- swift学习笔记3——类、结构体、枚举
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
- Java学习笔记-File类的基本方法
要渐渐养成写博客的习惯-----> 前段时间看Mars的java中的I/O流没怎么懂,发现I/O流好难啊.今天重新看一遍其他教学,还有书籍,做些笔记,记录下每天的学习生活. File类的一些方法 ...
- CSS3学习笔记——伪类hover
最近看到一篇文章:“Transition.Transform和Animation使用简介及应用展示” ,想看看里面 “不同缓动类效果demo”例子的效果,发现了一个问题如下: .Trans_Bo ...
- Java7编程 高级进阶学习笔记--嵌套类
定义: 在一个类中定义的类叫做嵌套类. 作用: 1.允许对相关类进行逻辑分组 2.增强了代码的封装性 3.使代码具有更强的可读性和维护性 使用方式: package com.cmz.baseTest; ...
随机推荐
- Oracle 存储过程判断语句正确写法和时间查询方法
判断语句:if 条件 then if 条件 then ************; elsif 条件 then ************; elsif 条件 then ***** ...
- Vue三步完成跨域请求
三步完成跨域请求 ①main.js中: Vue.prototype.HOME = '/api'; ② config/index.js中: module.exports = { dev: { // Pa ...
- Linux上安装jdk1.8和配置环境变量
前言 Linux 上安装jdk1.8 和配置环境变量,参考相关文档,本人在此总结,操作归纳如下. 第一步:创建jdk安装目录(该/usr/local/src 目录是空的,最好把我们自己下载的放到这,容 ...
- Making the Grade POJ - 3666
A straight dirt road connects two fields on FJ's farm, but it changes elevation more than FJ would l ...
- SQL Server 2012企业版和标准版的区别
关于使用Microsoft SQL Server 数据库的公司一般会有疑问,企业版数据库和标准版数据库的区别在哪?如果采购企业版的价格和标准版的价格相差很大,从多方资料查询发现,我认为最主要的区别是硬 ...
- Django-admin站点管理的详细使用
使用Django的管理模块,需要按照如下步骤操作: 管理界面本地化 创建管理员 注册模型类 自定义管理页面 1 管理界面本地化 在settings.py中设置语言和时区 LANGUAGE_CODE = ...
- Ubuntu和开发板用网线直连ping不通的问题
我装的Ubuntu 18.04双系统,在通过网络加载内核和文件系统那一步一直连接不上,uboot里面ping我的主机IP地址,提示: ping failed; host 192.168.1.111 i ...
- Node.js新手必须知道的4个JavaScript概念
如果只需要知道一种编程语言就可以构建一个全栈的应用程序,是不是特别了不起?Ryan Dahl为了把这个想法成为现实,创造了node.js.Node.js是建立在Chrome强劲的V8 JavaScri ...
- Struts2:搭建原理
记录下,struts2的搭建过程: 1核心jar包: struts-2.1.8\apps\struts2-blank-2.1.8.war 解压后 在struts2-blank-2.1.8\WEB-IN ...
- 自学maya三月,为啥还是95%都还不会,那是因为你不懂这几个技巧
有一些学员经常会有这种疑问,为什么学习MAYA软件这么难,为什么自己怎么学都学不会? 结果调查,发现了下面几个问题. 游戏建模 第一: 走弯路 很多人一开始学习Maya的时候肯定第一步是安装软件,但是 ...