北航c++复习笔记


这是老师做的大纲,复习将按照这个顺序

c++语法知识


(一)宏
1.常量宏

作用:消除神仙数

#define PI 3.14
2.函数宏

作用:小函数频繁调用

#define ADD(a,b) a+b
3.控制宏:开关

(1)“被重复引用”

是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的。比如:存在a.h文件#include "c.h"而此时b.cpp文件导入了#include “a.h” 和#include "c.h"此时就会造成c.h重复引用。

//在puu.h中有这样的定义
#ifndef PUU_H // 防止puu.h被重复引用 
#define PUU_H 
//coding……
#endif

(2)#pragma once

是一个比较常用的C/C++预处理指令,只要在头文件的最开始加入这条预处理指令,就能够保证头文件只被编译一次。但不是所有编译器都适用。和上一种方法差不多的意思。

4.补充

define中的三个特殊符号:#,##,#@

#define Conn(x,y) x##y
#define ToChar(x) #@x
#define ToString(x) #x
(二)库
dynamic: .a  .dll
static : .lib

区别:参不参与源程序的生成,exe包不包含这个库

封装


封装:隐藏对象的属性和实现的细节,仅公开对外接口

1.c++的类
class student
{
private:
    //attribute属性
    int age;
    char *name;
    char *addr;
    //基本的数据类型:数和串
    //method action
public:
    void init(char *a_name,int a_age);//方法的申明
    int get_age();
protected://和继承有关
};
//构造方法
void Student::init(char *a_name,int a_age){
	age=a_age;
	name=a_name;
}
//方法的定义
int Student::get_age(){
	return age;
}

数据成员是属性吗? 未必。“我作为一个人必须有flag吗?”
属性是数据成员吗? 是。
数据成员一定是类的组成部分吗? 未必。

2.类成员函数

成员函数可以定义在类定义内部(内联),或者单独使用范围解析运算符 :: 来定义。

class student
{
private:
    int age;
    char *name;
    char *addr;
public:
    int get_age(){
    	return age;
    }
};
///或者
int Student::get_age(){
	return age;
}
3.类的构造函数&析构函数
  • 构造函数的名称与类的名称是完全相同的,构造函数可用于为某些成员变量设置初始值。
  • 析构函数是一种特殊的成员函数,它会在每次删除所创建的对象时执行。当对象超出它的作用域时(包含对象的右括号),编译器将自动调用析构函数。
class Test
{
    int i;
    int *j;
    public:
    Test(int a,int b);
    Test(int a);
    ~Test();
}
Test::~Test(){
    delete j;//析构时,指针会自动消亡;指针所指内存需显式释放。
}
Test::Test(){
    this->j=NULL;
}
//构造函数:可能的多个
Test::Test(int a,int b){
    this->i=a;
    this->j=new int(b);//j是指针,指向内存
}
void main(){
    Test t(1,2);//显式传递参数,到括号自动消亡
    Test *p=new Test(1,2);//p局部变量,作用域结束消亡
    delete p;//目的是调用析构,消亡p指向的空间。删除p指的j,j指的new int
}

判断题
构造有new(分配空间)一定有析构–>>对
构造没有new一定没有析构–>>错,不代表其他没有new

还有可能是这个情况,注意delete了什么。

class Test
{
    int i;
    int *j;
    public:
    Test(int a);
    ~Test();
}
Test::~Test(){
    delete j;
}
//对象的消亡一定会执行~Test,而这没有被初始化
Test::Test(int a){
    this->i=a;
    this->j=NULL;//没有这个是错的,否则会delete野指针
}

还可以使用初始化列表来初始化字段

Test::Test(int a,int* b):i(a),j(b){
}
构造和析构别的知识
①构造函数的初始值有时必不可少

如果成员是const、引用,或者属于某种为提供默认构造函数的类类型,我们必须通过构造函数初始值列表为这些成员提供初值。

class ConstRef{
	public:
		ConstRef(int ii);
	private:
		int i;
		const int ci;
		int &ri;
};
//错误的构造函数
ConstRef::ConstRef(int ii){
	i=ii;
	ci=ii;
	ri=i;
}
//正确的构造函数
ConstRef::ConstRef(int ii):i(ii),ci(ii),ri(i){
}
int main(int argc,char*argv[]){
	ConstRef t(1);
}

错误构造函数的报错
在这里插入图片描述

②成员初始化的顺序

如果一个成员使用另一个成员来初始化,顺序就很关键了。
初始化是按照成员声明的顺序,所以需要保证顺序一致。

class X{
	int i;
	int j;
	public:
	//错误,但是我的编译器没报错
		X(int val):j(val),i(j){}
};
4.引用和指针
①区别
  • 不存在空引用。引用必须连接到一块合法的内存。
  • 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。(引用是对象的别名)
  • 引用必须在创建时被初始化;指针可以在任何时间被初始化。(这一点也很重要,相当于引用就是为了使用一个对象而生的。)e.g. int x;int& a=x;

总的来说引用和指针在用法上很像,都是去访问对象本身。但是由于很多人觉得Pointer is ugly!就把指针暴露地址的做法隐藏起来了,使得引用也能直接访问对象本身。

②函数的引用

引用的使用经常发生在函数参数和返回值中。
如果引用加了const,就是input参数(只读);不加就是output参数(可写)。
一个简单的例子

void fun(int& i){
    i++;  //长的貌似就是个值
}
int main(int argc,char*argv[]){
    int m =10;
    cout << m << endl;
    fun(m);//传递的是个引用 “引用就是披着羊皮的狼” “借值之名行指针之时”
    cout << m << endl;
}

常量引用–引用需要在创建时被初始化

void f(int& i){}
void g(const int& i){}//可以编译,但这个值不能被改变
int main(){
	//! f(1)
	g(1);
} 

指针引用–引用的对象是一个指针

void increment(int*& i ){i++;}
int main(){
	int *i=0;
	cout<<i<<endl;
	increment(i);
	cout<<i<<endl;
} 

结果:
0
0x4

③拷贝构造函数

对一个已知对象进行拷贝,编译系统会自动——拷贝构造函数,如果用户未定义拷贝构造函数,则会调用默认拷贝构造函数。
默认拷贝构造函数--浅拷贝(位拷贝):如果对象成员中有指针的话,这两个指针将会指向同一个内存空间。在程序结束时,调用析构函数,这块内存会被释放两次,造成内存泄漏!(第二个指针所指的内存被释放变成了野指针)典型的浅拷贝还有memcpy()。
自定义拷贝构造函数--深拷贝(逻辑拷贝):在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间。

class Test
{
    int i;
    int *j;
    public:
    Test(int a,int b);
    Test(Test& t);
    ~Test();
};
Test::Test(Test &t){
    this->i=t.i;
    this->j=new int(*t.j);
}
Test::Test(int a,int b){
    this->i=a;
    this->j=new int(b);//j是指针,指向内存
}
Test::~Test(){
    delete j;
}
int main(){
    Test t1(1,2);
    Test t2(t1);
}

析构的时候t2先死,t1后死。

5.static
  • static local variable:保值,main结束才释放
  • static global function:仅本文件调用
  • static data member:共享内存
    跨文件调用可以
  • extern int global;
  • extern void fun1();
    不能把全局变量放进头文件,因为有且一次定义。

static变量在第一次使用时被创建,结束时释放
(全局变量在程序开始之前创建)

void fun(){
    Test t1;//创建十次死亡十次
    static Test t2;//创建一次死亡一次
}

静态变量在静态区,全局变量在静态区
全局变量和静态变量放在一起。

int main(int argc, char* argv[]){
	int i;
	int j;
	static int m; 
	cout << &i << endl;
	cout << &j << endl;
	//i和j的地址差4,m不和它们在一起
	cout << &m << endl;
	return 0; 
}

程序间通信的方法

  • 函数调用
  • function member,data member
  • t1,t2 类中有static类型的变量
  • 4.static function member,函数成员

static函数

  • 与具体对象相关操作无关;
  • 操作static成员。

在 C++ 中,static 静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体的外部,通常在类的实现文件中初始化,如:double Account::Rate = 2.25;
static 关键字只能用于类定义体内部的声明中,定义时不能标示为 static

	Time t;
	t.GetCurrentTime();//不再依赖具体对象
	//也可以直接通过类名调用
	Time::GetCurrentTime();
class Test{
    public:
    	static int i;//属于本类全部对象。
        Test();
        static void a_fun();
};
int Test::i=10;//类的static int初始化
Test::Test(){
    cout <<"building"<<endl;
}
void Test::a_fun()
{
    //! this->i=10;error 调用者对象,static没对象
    i=20;
}
int main(){
	Test t;
	cout<<t.i<<endl;
	//等价 
	t.a_fun();
    Test::a_fun();
	cout<<t.i<<endl;
}

这里还需要注意一个问题:static const可以是private初始化。像static int i;是public的并且最好在类外(int Test::i=10)被初始化。

6.const

const 成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。

常常在运算符重载的时候使用。
operator type() const;
用const修饰方法内函数时,要求该方法不得修改成员属性。

void get_1() const{ return i;}

如果定义了常量对象,只能调用类里的常量方法。

class Test{
    int i;
public:
    void set_i(int ai){
        i=ai;//const 不能修饰这个方法
    }
    void get_1() const{ //const:本函数允许常量,前提方法内不允许改变属性
        return i;
    }
}
int main(char*[] argv,int argc){
     const Test t;//常量对象,不允许非常量方法
     t.get_i();//报错--》因为get_i极有可能修改数据
     return 0;
}
7.运算符重载
  • 不是必须的
  • 重载后要和人的基础认知相同,允许+不允许-
class Account{
    char* name;
    int id;
    //....
    int balance;
public:
    Account();
    void Save(int money);
    Account operator+(int money);
};

Account Account::operator+(int money){
    this->balance += money;
    return *this;
}

Account::Account(){
    balance=0;
}

void Account::Save(int money){
    this->balance += money;
}

int main(){
    Account a;
    a.Save(100);
    a=a+100;//把+理解成一个函数
}
8.new/delete vs. malloc/free
  • new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持。
  • 使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。
  • new,返回的是对象类型的指针;malloc返回的是void * ,需要通过强制类型转换。
  • malloc/free只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
class Memory{
public:
   //一个构造
   //一个析构
}
int main(char*[] argv,int argc){
    //new = malloc + constructor
    Memory *p = new Menory();
    Memory *p =(Memory*)malloc(sizeof(Memory));
    //delete = 析构 + free
}
9.friend

类可以允许其他类或者函数访问它的非公有成员。
friend关键词,类把一个函数作为它的友元。

class Box
{
   double width;
public:
   friend void printWidth( Box box );
   void setWidth( double wid );
};

// 成员函数定义
void Box::setWidth( double wid )
{
    width = wid;
}

// 请注意:printWidth() 不是任何类的成员函数
void printWidth( Box box )
{
   /* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
   cout << "Width of box : " << box.width <<endl;
}
 
// 程序的主函数
int main( )
{
   Box box;
   // 使用成员函数设置宽度
   box.setWidth(10.0);
   // 使用友元函数输出宽度
   printWidth( box );
   return 0;
}

声明类 ClassTwo 的所有成员函数作为类 ClassOne 的友元,需要在类 ClassOne 的定义中放置如下声明:

friend class ClassTwo;
10.this指针

在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。

//成员函数
int compare(Box box)
      {
         return this->Volume() > box.Volume();
      }
//调用的时候
int rt=Box1.compare(Box2);

继承


继承描述的是共性与特性。

1.reuse

共性–自底向上重写。
个性–子类可以定义父类没定义的。
java有且仅有一个父类,cpp可以有多个或者无父类。

class Borrower{
    int id;
    char *name;
public:
    void borrow();
    void returnbook();
};
void Borrower::borrow(){
   cout << "刷卡" << endl;
}
class Teacher : public Borrower{//继承语法
    //……
    //……
public:
    //……
}
void Teacher::borrow(){
    Borrower::borrower();//共性的体现
    cout << "借5本" << endl;
}
class Student{
    int id;
    char *name;
public:
    void borrow();
}

如果父类前没有public,变成了class Teacher : Borrower 相当于class Cat : private Pet 导致了父类的公有,子类变成了私有,使得人为削弱继承。(private的存在仅仅是维护语法完整性)

2.protected

父类的私有子类不能用,父类的public和protected可以用。

class Pet{
protected:
    int age;
    char *name;
public:
    void Speak()
    {
        cout << "speak" << endl;
    }
};

class Cat:public Pet{
public:
     void fun(){
         this->age=2;//父类的私有子类不能用,protected可以用
     }
     void Speak()//redifination
     {
         Pet::Speak();、
         cout << "miao" << endl;
     }
}
3.组合

变相继承,通过子类创建一个父类对象,调用父类的东西。

class Engine
{
public:
    void run(){
        cout << "engine is running " << endl;
    }
};

class Car
{
    Engine e;
    int other;
public:
    void run(){
        e.run();//reuse 子类和父类很可能是相同的,这个不是必须要有关系
        cout << "other run" <<endl;
    }
}

int main(){
    Cat c;
    c.Speak();
    return 0;
}
4.构造顺序
  • 每创造一个对象,会首先调用父类构造;消亡时会先析构子类,后析构父类。
  • 如果是上面组合的方式,也会先构造父类,再构造子类。
class Pet{
protected:
public:
    Pet(){
        cout << "Constructor Pet"<<endl;
    }
};
class Cat:public Pet{    
    int type;
public:
     Cat(){
        cout << "Constructor Cat"<<endl;
     }
};
int main(){
    Cat c;
    return 0;
}
5.子类构造方式
  • 如果子类只有默认构造,父类没有默认构造会报错。
  • 子类可以调用父类构造,也可以再写新的构造。
//父类构造
class Pet{
protected:
    int age;
    char *name;
Pet(int aage,char *aname){
        this->age=aage;
        this->name=aname;
        cout << "Construct Pet"<<endl;
}
}
class Cat:public Pet{    
    int type;
public:
//子类构造1
Cat() : Pet(2,"dudu")//显示的说明怎么构造
     {
        cout << "Construct Cat"<<endl;
     }
//子类构造2 
 Cat(int aage,char *aname,int atype):Pet(aage,aname),type(atype){
         // this->type=atype;
          cout << "Construct Cat"<<endl;
     }
}
6.多重继承的同名函数问题

如果两个父类有同名函数,子类的写法需要变化。

class Base1
{
public:
    void f(){}
};

class Base2
{
public:
    void h(){}
    void f(){}
};
class Derived : public Base1
{
    Base2 b2;
public:
    void f(){
        b2.f();
    }
}

老师说:“在你成为一个高手之前不要用多重继承,成为一个高手以后没有必要用多重继承。”

多态


1.基本语法

关键词:virtual
虚函数–>多态

  • virtual 有一块额外开辟的空间;每一个虚函数的类都一个虚函数表(v-table);
    每个对象有一个虚指针(v-ptr),在最前面的4字节,指向虚函数表。多个同类对象的指针是相同的。所以可以通过改虚指针,改里面的东西。
  1. 虚指针在构造函数时隐式初始化
  2. 传值就是传拷贝,会拷贝构造。传一个cat,拷贝构造成pet
  3. never pass by value
  4. 拷贝构造会破坏多态
  • 子类的相应函数会自动继承virtual关键词,习惯显式写上。
class Pet{
protected:
    int age;
    char *name;
public:
    virtual void Speak()
    {
        cout << "speak" << endl;
    }
};

多态有损性能–>空间–>每个对象多一个指针

2.多态的场景upcasting
  • binding : 绑定,将函数的一次调用,与函数入口相对应的
  • early binding:浅绑定,函数调用之前就决定了执行什么
  • later binding ,runtime binding ,dynamic binding:都一样,都指多态性
void Needle(Pet& pet)
{  
    pet.Speak();
}

int main(int argc,char*argv[]){
    //upcasting 向上类型转换
    cout << sizeof(Pet) << endl; 
    Cat cat;
    Needle(cat);//多态的使用
    return 0;
}
3.抽象基类

有纯虚函数的类是抽象基类,抽象基类不能被实例化,负责定义接口。
抽象类可以有数据成员,纯虚函数可以有函数体。
作用:

  • 是父类,可以当参数传递。不能传值,因为不能实例化,没有对象。
  • 纲领,子类中必须实现在父类中已有的方法。如果不重写父类中的纯虚函数,这个子类自动继承父类的纯虚函数,那么它还是抽象类,还是不能实例化。不能创建对象。
class Pet{//abstract class抽象类
protected:
    int age;
    char *name;
public:
    virtual void Speak()=0;//pure virtual纯虚函数
};
4.接口

应用抽象类,串联本不相关,却有行为上共性的类。

class FlyObject { 
public:
	virtual void fly() = 0;
};

class Airplane : public Machine, public FlyObject {
};

class Bird : public Animal, public FlyObject {
};
5.析构

构造函数不加virtual/static,通过类名直接访问,与对象无关
析构往往要加virtual(是多态的)

class MyClass{
public:
    MyClass();
    virtual ~MyClass();
} 

template


1.简单的例子
template <class T>//模板类
class Stack{
    T pool[100];
    int top;
public:
    Stack():top(0){}//初始化!!!!
    void Push(T i){
        //isfull
        pool[top++]=i;
    }
    T Pop(){
        return pool[--top];//后++前--
    }
}
//容器最适用于模板类型
int main (int argc,char*argv[]){
    Stack<int> s;
    for(int i=0;i<10;i++){
        s.Push(i);
    }
    for(int i=0;i<10;i++){
        cout << pool[i] << endl;
    }
}
2.STL
#include <vector>
#include <list>
using namespace std;
//1.万能容器
//2.动态增长
//工程不考虑性能问题
class Test{
    int a[10000];
public:
    Test(const Test&T){
        static int cnt=0;
        cout<<cnt<<endl;
    }
}
int main (int argc,char*argv[]){
   // vector<int> vi;
   // list<int> vi;
   list<Test> vi;
   vector<Test> vi;
   //vector 是先用,不够了找一块更大的搬家
   //list 来一块分一块
    for(int i=0;i<10000;i++){
        Test t;
        vi.push_back(t);
    }
   //iterator迭代器
    vector<int>::iterator it = vi.begin;
    while(it!=vi.end()){
        cout<<*it<<Endl;
        it++;
    }
}

设计模式


1.单例模式
class Singleton{
public:
	static Singleton* getInstance();

private:
	Singleton();
	//把复制构造函数和=操作符也设为私有,防止被复制
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
	static Singleton* instance;
};

Singleton::Singleton(){}
Singleton::Singleton(const Singleton&){}
Singleton& Singleton::operator=(const Singleton&){}

//在此处初始化
Singleton* Singleton::instance = new Singleton();
Singleton* Singleton::getInstance(){
	return instance;
}
 
int main(){
	Singleton* singleton1 = Singleton::getInstance();
	Singleton* singleton2 = Singleton::getInstance();
	if (singleton1 == singleton2)
		fprintf(stderr,"singleton1 = singleton2\n");
	return 0;
}
2.工厂模式

AbstractProduct父类:

class AbstractProduct{
public:
    AbstractProduct();
    virtual ~AbstractProduct();    
public:
    virtual void operation() = 0;
};

两种产品的子类

class ProductA:public AbstractProduct{

public:
    ProductA();
    virtual ~ProductA();
    
public:
    void operation();
};

class ProductB:public AbstractProduct{

public:
    ProductB();
    virtual ~ProductB();
    
public:
    void operation();
};

AbstractFactory类

class AbstractFactory{

public:
    AbstractFactory();
    virtual ~AbstractFactory();
    
public:
//重点,返回的是AbstractProduct*
    virtual AbstractProduct* createProduct(int type) = 0;    
};


class SimpleFactory:public AbstractFactory{

public:
    SimpleFactory();
    ~SimpleFactory();
    
public:
    AbstractProduct* createProduct(int type);
};

main:

int main(){
    AbstractFactory* factory = new SimpleFactory();
    AbstractProduct* product = factory->createProduct(1);
    product->operation();
    delete product;
    product = NULL;
    
    product = factory->createProduct(2);
    product->operation();
    delete product;
    product = NULL;
    return 0;
}
3.观察者模式

接口
Subject(目标)
——目标知道它的观察者。可以有任意多个观察者观察同一个目标;
——提供注册和删除观察者对象的接口。
Observer(观察者)
——为那些在目标发生改变时需获得通知的对象定义一个更新接口。

实现类
ConcreteSubject(具体目标)
——将有关状态存入各ConcreteObserver对象;
——当它的状态发生改变时,向它的各个观察者发出通知。
ConcreteObserver(具体观察者)
——维护一个指向ConcreteSubject对象的引用;
——存储有关状态,这些状态应与目标的状态保持一致;
——实现Observer的更新接口以使自身状态与目标的状态保持一致。

接口:

class Observer
{
public:
    virtual void Update(int) = 0;
};
 
class Subject
{
public:
    virtual void Attach(Observer *) = 0;
    virtual void Detach(Observer *) = 0;
    virtual void Notify() = 0;
};

实现类:

class ConcreteObserver : public Observer
{
public:
    ConcreteObserver(Subject *pSubject) : m_pSubject(pSubject){}
 
    void Update(int value)
    {
        cout << "ConcreteObserver get the update. New State:" << value << endl;
    }
 
private:
    Subject *m_pSubject;
};
 
class ConcreteObserver2 : public Observer
{
public:
    ConcreteObserver2(Subject *pSubject) : m_pSubject(pSubject){}
 
    void Update(int value)
    {
        cout << "ConcreteObserver2 get the update. New State:" << value << endl;
    }
 
private:
    Subject *m_pSubject;
};
 
class ConcreteSubject : public Subject
{
public:
    void Attach(Observer *pObserver);
    void Detach(Observer *pObserver);
    void Notify();
 
    void SetState(int state)
    {
        m_iState = state;
    }
 
private:
    std::list<Observer *> m_ObserverList;
    int m_iState;
};
 
void ConcreteSubject::Attach(Observer *pObserver)
{
    m_ObserverList.push_back(pObserver);
}
 
void ConcreteSubject::Detach(Observer *pObserver)
{
    m_ObserverList.remove(pObserver);
}
 
void ConcreteSubject::Notify()
{
    std::list<Observer *>::iterator it = m_ObserverList.begin();
    while (it != m_ObserverList.end())
    {
        (*it)->Update(m_iState);
        ++it;
    }
}

main:

int main()
{
    // Create Subject
    ConcreteSubject *pSubject = new ConcreteSubject();
 
    // Create Observer
    Observer *pObserver = new ConcreteObserver(pSubject);
    Observer *pObserver2 = new ConcreteObserver2(pSubject);
 
    // Change the state
    pSubject->SetState(2);
 
    // Register the observer
    pSubject->Attach(pObserver);
    pSubject->Attach(pObserver2);
 
    pSubject->Notify();
 
    // Unregister the observer
    pSubject->Detach(pObserver);
 
    pSubject->SetState(3);
    pSubject->Notify();
 
    delete pObserver;
    delete pObserver2;
    delete pSubject;
}

乱七八糟的cpp知识合集


(一)#pragma pack(n)

默认的语法是:按照结构体中最大的size对齐
对结构体等添加程序员自定义的对齐规则。

struct Test{
	int i;
	double j;
	char c;
};

size=24(double=8,8*3=24)
如果按照#pragma pack(1),则按照1对齐

#pragma pack(1)
struct Test{
	int i;
	double j;
	char c;
};

size=13(int=4,doube=8,char=1)

(二)关于函数
① 重载(overloading)

同名函数,不同参数。(不能以同名函数,不同返回值的方式)

void Print(int i)
{
    cout << i << endl;
    //console out 控制台输出
}
void Print(char * str)
{
    cout << str << endl;
}
int main(int argc, char* argv[])
{
    Print(1);
    Print("Hello");
}
② 默认参数(default parameter)

规则:

  • 默认的参数必须放后面;
  • 头文件与源文件成对出现;
  • 应用的时候,如果有默认参数,一概不传递。
void fun(int a,int b=10,int c=20);
//如果没有传参结果是默认值,如果传了则是传了的值。
③ 占位符

占位参数,一般用于overloading:凑同名函数不同参数;
传啥都行>>MUST BE ZERO

void fun(int)
{
}
int main(int argc, char* argv[])
{
   fun(1);
   return 0;
}
(三)成员变量的初始化方法
  • 普通私有成员变量:类构造器初始(可初始值,可赋值)
  • const私有成员变量:Test::Test(void):var1(11111),var2(22222){}类构造器初始,only初始值不能省
  • static私有成员变量:int Test::var3 = 3333333;自己定义构造
  • static const私有成员变量:static const int var4=4444;
-----------------Test.h----------------------------
#pragma once
class Test
{
private :
    int var1;
    // int var11= 4; 错误的初始化方法
    const int var2 ;
    // const int var22 =22222; 错误的初始化方法
    static int var3;
    // static int var3333=33333; 错误,只有静态常量int成员才能直接赋值来初始化
    static const int var4=4444; //正确,静态常量成员可以直接初始化    
    static const int var44;
public:
    Test(void);
    ~Test(void);
};
--------------------Test.cpp-----------------------------------
#include ".\test.h"
 
int Test::var3 = 3333333; //静态成员的正确的初始化方法
 
// int Test::var1 = 11111;; 错误静态成员才能初始化
// int Test::var2 = 22222; 错误
// int Test::var44 = 44444; // 错误的方法,提示重定义
Test::Test(void)var1(11111),var2(22222)正确的初始化方法//var3(33333)不能在这里初始化
{
    var1 =11111; //正确, 普通变量也可以在这里初始化
    //var2 = 222222; 错误,因为常量不能赋值,只能在 "constructor initializer (构造函数的初始化列表)" 那里初始化
          
    var3 =44444; //这个赋值是正确的,不过因为所有对象一个静态成员,所以会影响到其他的,这不能叫做初始化了吧
}
Test::~Test(void){}
(四)c补充知识
①char* 和 char[]
  • char [],表示的是一个char类型的数组指针,该指针所指向的数组内容是保存在栈上面的,是可以修改的。
  • char *是一个字符串指针,这个指针指向的是字符串第一个字符的地址,而这个指针存在栈上,但是字符串的内容并不在栈里面,而在字符常量区域里面储存。
  • 查看char *str1 = "abcd1234"与char *str2 = "abcd1234"的地址时,他们都是储存的字符a的地址,所以这个地址时相同的,其 %p 的值也是一样的。
  • 但是char str3[] = “abcd1234” 与 char str[] = "abcd1234"是分别两个char类型的数组,而str3与str4分别表示的是char型的数组指针,所以他们的地址时不同的。
  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值