C++ Primer 笔记——类

1.定义在类内部的函数是隐式的inline函数。

2.因为this的目的总是指向“这个”对象,所以this是一个常量指针,我们不允许改变this中保存的地址。

3.常量成员函数:允许把const关键字放在成员函数的参数列表之后,此时紧跟在参数列表后面的const表示this是一个指向常量的指针。因为this是指向常量的指针,所以常量成员函数不能改变调用它的对象的内容。

4.常量对象,以及常量对象的引用或指针都只能调用常量成员函数。

5.编译器分两步处理类:首先编译成员的声明,然后才轮到成员函数体。因此成员函数体可以随意使用类中的其他成员而无须在意这些成员出现的次序。

6.构造函数不能被声明成const,当我们创建类的一个const对象时,直到构造函数完成初始化过程,对象才能真正取得其“常量”属性。因此,构造函数在const对象的构造过程中可以向其写值。

7.当类没有声明任何构造函数时,编译器才会自动的生成默认构造函数,默认构造函数按照如下规则初始化类的数据成员:

  如果存在类内的初始值,用它来初始化成员。

  否则,默认初始化该成员。

8.我们可以使用default来要求编译器生产构造函数。

class test
{
public:
    test() = default;
    test(int i) {}
};

9.使用class和struct定义类唯一的区别就是默认访问权限,class默认是private,struct默认是public。

10.类允许其他类或者函数访问它的非公有成员,方法是另其他类或者函数成为它的友元,友元声明只能出现在类定义的内部,也不受访问控制符限制。

class test
{
    friend int GetTestCount(const test& t);
public:
    test() = default;
private:
    int m_count;
};

int GetTestCount(const test& t)
{
    return t.m_count;
}

11.尽管当类的定义发生改变时无需更改用户代码,但是使用了该类的源文件必须重新编译。

12.一个可变数据成员永远不会是const,即使它是const对象的成员。因此一个const成员函数可以改变一个可变成员的值。

class test
{
public:
    test() = default;
    void change_count() const { m_count++; }
private:
    mutable int m_count;
};

13.一个const成员函数如果以引用的形式返回*this,那么它的返回类型将是常量引用。

class test
{
public:
    test() = default;
    const test& change_count() const { return *this; }
};

14.每个类负责控制自己的友元类或者友元函数。

class test
{
    friend class best;
private:
    int m_count;
};

class best
{
public:
    int get_test_count() { return m_t.m_count; }
private:
    test m_t;
};
class test;
class best
{
public:
    int get_test_count(const test& t);
};

class test
{
    friend int best::get_test_count(const test& t);
private:
    int m_count;
};

// 注意这个定义必须在test定义之后,否则不知道test类里的细节会报错
int best::get_test_count(const test& t)
{
    return t.m_count;
}

15.和其它局部作用域不同,在类中不能重复定义外层作用域中已经定义过的类型名字,即使定义相同。

typedef int my_int;

class test
{
private:
    typedef int my_int;        // 错误,重复定义,但是有的编译器可以编译过
    my_int m_count;
};

16.成员函数中使用名字时的查找顺序是:成员函数->类->类外作用域。

int count;
class test
{
private:
    int count;
    int add_count(int count) {
        this->count += count;    // this->count指的是类的成员,count指的是参数
        ::count++;                // ::count指的是外层作用域的count
    }
};

17.如果没有在构造函数的初始化列表中初始化成员,则在构造函数体之前执行默认初始化。

18.如果类的成员是const或者引用或者没有默认构造函数的类类型的话,必须在定义时或者初始化列表里完成初始化。

class test
{
public:
    test(){};    // 错误,m_const没有初始化
private:
    const int m_count;
};

19.构造函数的初始化列表中成员的出现顺序并不代表初始化的顺序,而是按照在类中的定义顺序来初始化。所以如果一个成员靠另一个成员来初始化的时候要注意。

class test
{
public:
    test()
        :m_base(0)            // m_base的值是0
        ,m_count(m_base)    // m_count的值是未定义的
    {};
private:
    int m_count;
    int m_base;
};

20.如果一个构造函数为所有的参数都定义了默认实参,则这个类实际上定义了默认构造函数。

21.委托构造函数指的是一个构造函数调用其它构造函数来执行自己初始化的过程。

class test
{
public:
    test(int i,int j) { }        // 非委托构造函数
    test(int i) :test(i, 0) {}    // 委托构造函数,委托了test(int,int)构造函数
    test(int i) :test() {}        // 委托构造函数,委托了默认构造函数
};

22.当已有的构造函数中没有完全初始化所有变量的时候会执行默认构造函数。

class test
{
public:
    test(int i) {}
private:
    int m_count;
};

struct A
{
    test t;
};

A a;        // 错误,不能为A合成默认构造函数

struct B
{
    B() {}    // 错误,t没有初始值
    test t;
};
class test
{
public:
    test(int i) {}
};

test t();    // 正确,t是声明的一个函数
test t1;    // 错误,test没有默认构造函数

23.如果构造函数之接受一个实参,则它实际上定义了转换为此类类型的转换构造函数,但是这种转换只允许一步类类型转换。

class test
{
public:
    test(std::string str) {}
};

void get_test(test t){}

get_test("123");                // 错误,"123"既不是test类型,也不是string类型
get_test(std::string("123"));    // 正确,string类型被转换为test类型

但是其实上述操作完成转换后我们并不能使用它,如果不想这种转换可以用explicit

class test
{
public:
    explicit test(std::string str) {}
};

void get_test(test t){}

get_test(std::string("123"));    // 错误,不允许这种隐式转换

24.聚合类必须满足以下条件:

  • 所有成员都是public
  • 没有定义任何构造函数
  • 没有类内初始值
  • 没有基类,也没有virtual函数
struct test
{
    std::string name;
    int count;
};
test t = { "test",1 };

25.数据成员都是字面值类型的聚合类是字面值常量类,或者符合下列条件的也是字面值常量类:

  • 数据类型都必须是常量类型
  • 类必须至少含有一个constexpr构造函数
  • 如果一个数据成员含有类内初始值,则内置类型成员的初始值必须是一条常量表达式;或者如果成员属于某种类类型,则初始值必须使用成员自己的constexpr构造函数
  • 类必须使用析构函数的默认定义,该成员负责销毁类的对象

因为构造函数没有返回语句,而constexpr函数的唯一可执行语句就是返回语句,可以推断出constexpr构造函数其实是空语句。

constexpr构造函数必须初始化所有数据成员,初始值或者使用constexpr构造函数,或者是一条常量表达式。

26.类的静态成员存在于任何对象之外,对象中部包含任何与静态数据成员有关的数据。类似的,静态成员函数也不与任何对象绑定在一起,也不包含this指针。作为结果,静态成员函数不能声明成const的。

27.一般来说我们不能在类内部初始化静态成员,在类的外部定义静态成员的时候,不能重复static关键字,而且一旦被定义,将一直存在于程序的整个生命周期中。

28.静态成员和普通成员的区别:

class test
{
    test(int i = m_count);        // static成员可以做默认实参,普通成员不可以
private:
    static int m_count;
    static test m_t;            // 正确,静态成员可以是不完全类型
    test* m_t1;                    // 正确,指针可以是不完全类型
    test m_t2;                    // 错误,数据成员必须是完全类型
};
时间: 2024-12-28 23:46:47

C++ Primer 笔记——类的相关文章

C++ Primer 笔记——类成员指针

1.当我们初始化一个成员指针或为成员指针赋值时,该指针并没有指向任何数据.成员指针指定了成员而非成员所属的对象,只有当解引用成员指针时,我们才提供对象信息. 2.和普通的函数指针类似,如果成员存在重载的问题,则我们必须显示地声明函数类型以明确指出我们想要使用的是哪个函数.和使用指向数据成员的指针一样,我们使用 .* 或者 ->*运算符作用于指向成员函数的指针. class test { public: void Add(int){} void Add(long){} void get() {}

C++ Primer笔记4_类的静态成员_IO库

1.类的静态成员 static成员变量与函数 static成员变量:必须在类外初始化:(const或引用类型变量必须在构造函数初始化列表里初始化) static成员函数: 不依赖于类,相当于类里的全局函数(可以由该类对象调用,也可以 类名::函数名()的形式调用) 不包含this指针,不能声明为const,声明为const表示不会改变对象,而static成员函数存在于任何对象之外. 相当于把访问范围限制在所在的类中!  注意:不能访问类中非static成员变量以及非static成员函数. 注意:

0713-----C++Primer听课笔记----------类和对象

1.要求:将一篇文章录入,然后实现查询: a)查询可以输入字符或者字符串 b)将包含它们的单词输出 c)允许重复 d)如果输入查询有空格,当多个处理 Ex: 输入ro,应该打印出“microsift” 输入”he wr”,则查询两次 1.1  不用类实现 #include <iostream> #include <string> #include <vector> #include <fstream> #include <stdexcept> u

C++ Primer笔记3_默认实参_类初探_名字查找与类的作用域

1.默认函数实参 在C++中,可以为参数指定默认值,C语言是不支持默认参数的,Java也不支持! 默认参数的语法与使用: (1)在函数声明或定义时,直接对参数赋值.这就是默认参数: (2)在函数调用时,省略部分或全部参数.这时可以用默认参数来代替. 注意事项: (1)函数默认值只能赋值一次,或者是在声明中,或者是在定义中,都可以. (2)默认参数定义的顺序为自右到左.即如果一个参数设定了缺省值时,其右边的参数都要有缺省值.比如int f(int a, int b=1,int c=2,int d=

C++ Primer笔记8_动态内存_智能指针

1.动态内存 C++中,动态内存管理是通过一对运算符完成的:new和delete.C语言中通过malloc与free函数来实现先动态内存的分配与释放.C++中new与delete的实现其实会调用malloc与free. new分配: 分配变量空间: int *a = new int; // 不初始化 int *b = new int(10); //初始化为10 string *str = new string(10, ); 分配数组空间: int *arr = new int[10];//分配的

C++ Primer笔记12_运算符重载_递增递减运算符_成员访问运算符

1.递增递减运算符 C++语言并不要求递增递减运算符必须是类的成员.但是因为他们改变的正好是所操作对象的状态,所以建议设定为成员函数. 对于递增与递减运算符来说,有前置与后置两个版本,因此,我们应该为类定义两个版本的递增与递减运算符. 问题来了,程序是如何区分前置和后置呢?因为都是++和-- 为了解决这个问题,后置版本的递增递减运算符接受一个额外的(不被使用)int类型的形参.当我们使用后置运算符时,编译器为这个形参提供一个值为0的实参.这个形参唯一的作用就是区分前置和后置运算符函数. 因为不会

C++ Primer笔记2_四种类型转换_异常机制

1.类型转换 命名的强制类型转换: 有static_cast.dynamic_cast.const_cast.reinterpret_cast static_cast: 编译器隐式执行的任何类型转换都可以由static_cast完成 当一个较大的算术类型赋值给较小的类型时,可以用static_cast进行强制转换. 可以将void*指针转换为某一类型的指针 可以将基类指针强制转换为派生类指针,但是不安全. 无法将const转化为nonconst,这个只有const_cast才可以办得到 举例:

重读C++ Primer笔记

C++ Primer 5E 有符号和无符号 无符号类型和有符号类型混合运算时,有符号数会被提升至无符号类型,如果其值为负责会产生错误. int main() { unsigned int u = 10; int i = -42; std::cout<<u+i<< std::endl; // 4294967264 if sizeof(int)==4 return 0; } 列表初始化 列表初始化过程不允许损失数据类型精度,所以下面代码中的两行无法通过编译 int main() { d

C++ Primer笔记13_运算符重载_总结

总结: 1.不能重载的运算符: . 和 .* 和 ?: 和 ::  和 sizeof 和 typeid 2.重载运算符有两种基本选择: 类的成员函数或者友元函数, 建议规则如下: 运算符 建议使用 所有一元运算符 成员函数 = () [] -> 必须是成员函数 += -= /= *= ^= &= != %= >>= <<= , 似乎带等号的都在这里了. 成员函数 所有其它二元运算符, 例如: –,+,*,/ 友元函数 3.前几篇中的实例,现在汇总Person类的程序: